entitlements 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/deploy-entitlements +10 -1
  4. data/lib/contracts-ruby2/CHANGELOG.markdown +115 -0
  5. data/lib/contracts-ruby2/Gemfile +17 -0
  6. data/lib/contracts-ruby2/LICENSE +23 -0
  7. data/lib/contracts-ruby2/README.md +108 -0
  8. data/lib/contracts-ruby2/Rakefile +8 -0
  9. data/lib/contracts-ruby2/TODO.markdown +6 -0
  10. data/lib/contracts-ruby2/TUTORIAL.md +773 -0
  11. data/lib/contracts-ruby2/benchmarks/bench.rb +67 -0
  12. data/lib/contracts-ruby2/benchmarks/hash.rb +69 -0
  13. data/lib/contracts-ruby2/benchmarks/invariants.rb +91 -0
  14. data/lib/contracts-ruby2/benchmarks/io.rb +62 -0
  15. data/lib/contracts-ruby2/benchmarks/wrap_test.rb +57 -0
  16. data/lib/contracts-ruby2/contracts.gemspec +17 -0
  17. data/lib/contracts-ruby2/cucumber.yml +1 -0
  18. data/lib/contracts-ruby2/dependabot.yml +20 -0
  19. data/lib/contracts-ruby2/features/README.md +17 -0
  20. data/lib/contracts-ruby2/features/basics/functype.feature +71 -0
  21. data/lib/contracts-ruby2/features/basics/pretty-print.feature +241 -0
  22. data/lib/contracts-ruby2/features/basics/simple_example.feature +210 -0
  23. data/lib/contracts-ruby2/features/builtin_contracts/README.md +22 -0
  24. data/lib/contracts-ruby2/features/builtin_contracts/and.feature +103 -0
  25. data/lib/contracts-ruby2/features/builtin_contracts/any.feature +44 -0
  26. data/lib/contracts-ruby2/features/builtin_contracts/args.feature +80 -0
  27. data/lib/contracts-ruby2/features/builtin_contracts/array_of.feature +1 -0
  28. data/lib/contracts-ruby2/features/builtin_contracts/bool.feature +64 -0
  29. data/lib/contracts-ruby2/features/builtin_contracts/enum.feature +1 -0
  30. data/lib/contracts-ruby2/features/builtin_contracts/eq.feature +1 -0
  31. data/lib/contracts-ruby2/features/builtin_contracts/exactly.feature +1 -0
  32. data/lib/contracts-ruby2/features/builtin_contracts/func.feature +1 -0
  33. data/lib/contracts-ruby2/features/builtin_contracts/hash_of.feature +1 -0
  34. data/lib/contracts-ruby2/features/builtin_contracts/int.feature +93 -0
  35. data/lib/contracts-ruby2/features/builtin_contracts/keyword_args.feature +1 -0
  36. data/lib/contracts-ruby2/features/builtin_contracts/maybe.feature +1 -0
  37. data/lib/contracts-ruby2/features/builtin_contracts/nat.feature +115 -0
  38. data/lib/contracts-ruby2/features/builtin_contracts/nat_pos.feature +119 -0
  39. data/lib/contracts-ruby2/features/builtin_contracts/neg.feature +115 -0
  40. data/lib/contracts-ruby2/features/builtin_contracts/none.feature +145 -0
  41. data/lib/contracts-ruby2/features/builtin_contracts/not.feature +1 -0
  42. data/lib/contracts-ruby2/features/builtin_contracts/num.feature +64 -0
  43. data/lib/contracts-ruby2/features/builtin_contracts/or.feature +83 -0
  44. data/lib/contracts-ruby2/features/builtin_contracts/pos.feature +116 -0
  45. data/lib/contracts-ruby2/features/builtin_contracts/range_of.feature +1 -0
  46. data/lib/contracts-ruby2/features/builtin_contracts/respond_to.feature +78 -0
  47. data/lib/contracts-ruby2/features/builtin_contracts/send.feature +147 -0
  48. data/lib/contracts-ruby2/features/builtin_contracts/set_of.feature +1 -0
  49. data/lib/contracts-ruby2/features/builtin_contracts/xor.feature +99 -0
  50. data/lib/contracts-ruby2/features/support/env.rb +6 -0
  51. data/lib/contracts-ruby2/lib/contracts/attrs.rb +24 -0
  52. data/lib/contracts-ruby2/lib/contracts/builtin_contracts.rb +542 -0
  53. data/lib/contracts-ruby2/lib/contracts/call_with.rb +108 -0
  54. data/lib/contracts-ruby2/lib/contracts/core.rb +52 -0
  55. data/lib/contracts-ruby2/lib/contracts/decorators.rb +47 -0
  56. data/lib/contracts-ruby2/lib/contracts/engine/base.rb +136 -0
  57. data/lib/contracts-ruby2/lib/contracts/engine/eigenclass.rb +50 -0
  58. data/lib/contracts-ruby2/lib/contracts/engine/target.rb +70 -0
  59. data/lib/contracts-ruby2/lib/contracts/engine.rb +26 -0
  60. data/lib/contracts-ruby2/lib/contracts/errors.rb +71 -0
  61. data/lib/contracts-ruby2/lib/contracts/formatters.rb +136 -0
  62. data/lib/contracts-ruby2/lib/contracts/invariants.rb +68 -0
  63. data/lib/contracts-ruby2/lib/contracts/method_handler.rb +187 -0
  64. data/lib/contracts-ruby2/lib/contracts/method_reference.rb +100 -0
  65. data/lib/contracts-ruby2/lib/contracts/support.rb +61 -0
  66. data/lib/contracts-ruby2/lib/contracts/validators.rb +139 -0
  67. data/lib/contracts-ruby2/lib/contracts/version.rb +3 -0
  68. data/lib/contracts-ruby2/lib/contracts.rb +281 -0
  69. data/lib/contracts-ruby2/script/docs-release +3 -0
  70. data/lib/contracts-ruby2/script/docs-staging +3 -0
  71. data/lib/contracts-ruby2/script/rubocop.rb +5 -0
  72. data/lib/contracts-ruby2/spec/attrs_spec.rb +119 -0
  73. data/lib/contracts-ruby2/spec/builtin_contracts_spec.rb +461 -0
  74. data/lib/contracts-ruby2/spec/contracts_spec.rb +770 -0
  75. data/lib/contracts-ruby2/spec/fixtures/fixtures.rb +730 -0
  76. data/lib/contracts-ruby2/spec/invariants_spec.rb +17 -0
  77. data/lib/contracts-ruby2/spec/methods_spec.rb +54 -0
  78. data/lib/contracts-ruby2/spec/module_spec.rb +18 -0
  79. data/lib/contracts-ruby2/spec/override_validators_spec.rb +162 -0
  80. data/lib/contracts-ruby2/spec/ruby_version_specific/contracts_spec_1.9.rb +24 -0
  81. data/lib/contracts-ruby2/spec/ruby_version_specific/contracts_spec_2.0.rb +55 -0
  82. data/lib/contracts-ruby2/spec/ruby_version_specific/contracts_spec_2.1.rb +63 -0
  83. data/lib/contracts-ruby2/spec/spec_helper.rb +102 -0
  84. data/lib/contracts-ruby2/spec/support.rb +10 -0
  85. data/lib/contracts-ruby2/spec/support_spec.rb +21 -0
  86. data/lib/contracts-ruby2/spec/validators_spec.rb +47 -0
  87. data/lib/contracts-ruby3/CHANGELOG.markdown +117 -0
  88. data/lib/contracts-ruby3/Gemfile +21 -0
  89. data/lib/contracts-ruby3/LICENSE +23 -0
  90. data/lib/contracts-ruby3/README.md +114 -0
  91. data/lib/contracts-ruby3/Rakefile +10 -0
  92. data/lib/contracts-ruby3/TODO.markdown +6 -0
  93. data/lib/contracts-ruby3/TUTORIAL.md +773 -0
  94. data/lib/contracts-ruby3/benchmarks/bench.rb +67 -0
  95. data/lib/contracts-ruby3/benchmarks/hash.rb +69 -0
  96. data/lib/contracts-ruby3/benchmarks/invariants.rb +91 -0
  97. data/lib/contracts-ruby3/benchmarks/io.rb +62 -0
  98. data/lib/contracts-ruby3/benchmarks/wrap_test.rb +57 -0
  99. data/lib/contracts-ruby3/contracts.gemspec +20 -0
  100. data/lib/contracts-ruby3/cucumber.yml +1 -0
  101. data/lib/contracts-ruby3/dependabot.yml +20 -0
  102. data/lib/contracts-ruby3/features/README.md +17 -0
  103. data/lib/contracts-ruby3/features/basics/functype.feature +71 -0
  104. data/lib/contracts-ruby3/features/basics/pretty-print.feature +241 -0
  105. data/lib/contracts-ruby3/features/basics/simple_example.feature +210 -0
  106. data/lib/contracts-ruby3/features/builtin_contracts/README.md +22 -0
  107. data/lib/contracts-ruby3/features/builtin_contracts/and.feature +103 -0
  108. data/lib/contracts-ruby3/features/builtin_contracts/any.feature +44 -0
  109. data/lib/contracts-ruby3/features/builtin_contracts/args.feature +80 -0
  110. data/lib/contracts-ruby3/features/builtin_contracts/array_of.feature +1 -0
  111. data/lib/contracts-ruby3/features/builtin_contracts/bool.feature +64 -0
  112. data/lib/contracts-ruby3/features/builtin_contracts/enum.feature +1 -0
  113. data/lib/contracts-ruby3/features/builtin_contracts/eq.feature +1 -0
  114. data/lib/contracts-ruby3/features/builtin_contracts/exactly.feature +1 -0
  115. data/lib/contracts-ruby3/features/builtin_contracts/func.feature +1 -0
  116. data/lib/contracts-ruby3/features/builtin_contracts/hash_of.feature +1 -0
  117. data/lib/contracts-ruby3/features/builtin_contracts/int.feature +93 -0
  118. data/lib/contracts-ruby3/features/builtin_contracts/keyword_args.feature +1 -0
  119. data/lib/contracts-ruby3/features/builtin_contracts/maybe.feature +1 -0
  120. data/lib/contracts-ruby3/features/builtin_contracts/nat.feature +115 -0
  121. data/lib/contracts-ruby3/features/builtin_contracts/nat_pos.feature +119 -0
  122. data/lib/contracts-ruby3/features/builtin_contracts/neg.feature +115 -0
  123. data/lib/contracts-ruby3/features/builtin_contracts/none.feature +145 -0
  124. data/lib/contracts-ruby3/features/builtin_contracts/not.feature +1 -0
  125. data/lib/contracts-ruby3/features/builtin_contracts/num.feature +64 -0
  126. data/lib/contracts-ruby3/features/builtin_contracts/or.feature +83 -0
  127. data/lib/contracts-ruby3/features/builtin_contracts/pos.feature +116 -0
  128. data/lib/contracts-ruby3/features/builtin_contracts/range_of.feature +1 -0
  129. data/lib/contracts-ruby3/features/builtin_contracts/respond_to.feature +78 -0
  130. data/lib/contracts-ruby3/features/builtin_contracts/send.feature +147 -0
  131. data/lib/contracts-ruby3/features/builtin_contracts/set_of.feature +1 -0
  132. data/lib/contracts-ruby3/features/builtin_contracts/xor.feature +99 -0
  133. data/lib/contracts-ruby3/features/support/env.rb +8 -0
  134. data/lib/contracts-ruby3/lib/contracts/attrs.rb +26 -0
  135. data/lib/contracts-ruby3/lib/contracts/builtin_contracts.rb +575 -0
  136. data/lib/contracts-ruby3/lib/contracts/call_with.rb +119 -0
  137. data/lib/contracts-ruby3/lib/contracts/core.rb +54 -0
  138. data/lib/contracts-ruby3/lib/contracts/decorators.rb +50 -0
  139. data/lib/contracts-ruby3/lib/contracts/engine/base.rb +137 -0
  140. data/lib/contracts-ruby3/lib/contracts/engine/eigenclass.rb +51 -0
  141. data/lib/contracts-ruby3/lib/contracts/engine/target.rb +72 -0
  142. data/lib/contracts-ruby3/lib/contracts/engine.rb +28 -0
  143. data/lib/contracts-ruby3/lib/contracts/errors.rb +74 -0
  144. data/lib/contracts-ruby3/lib/contracts/formatters.rb +140 -0
  145. data/lib/contracts-ruby3/lib/contracts/invariants.rb +72 -0
  146. data/lib/contracts-ruby3/lib/contracts/method_handler.rb +197 -0
  147. data/lib/contracts-ruby3/lib/contracts/method_reference.rb +102 -0
  148. data/lib/contracts-ruby3/lib/contracts/support.rb +63 -0
  149. data/lib/contracts-ruby3/lib/contracts/validators.rb +143 -0
  150. data/lib/contracts-ruby3/lib/contracts/version.rb +5 -0
  151. data/lib/contracts-ruby3/lib/contracts.rb +290 -0
  152. data/lib/contracts-ruby3/script/docs-release +3 -0
  153. data/lib/contracts-ruby3/script/docs-staging +3 -0
  154. data/lib/contracts-ruby3/script/rubocop.rb +5 -0
  155. data/lib/contracts-ruby3/spec/attrs_spec.rb +119 -0
  156. data/lib/contracts-ruby3/spec/builtin_contracts_spec.rb +457 -0
  157. data/lib/contracts-ruby3/spec/contracts_spec.rb +773 -0
  158. data/lib/contracts-ruby3/spec/fixtures/fixtures.rb +725 -0
  159. data/lib/contracts-ruby3/spec/invariants_spec.rb +17 -0
  160. data/lib/contracts-ruby3/spec/methods_spec.rb +54 -0
  161. data/lib/contracts-ruby3/spec/module_spec.rb +18 -0
  162. data/lib/contracts-ruby3/spec/override_validators_spec.rb +162 -0
  163. data/lib/contracts-ruby3/spec/ruby_version_specific/contracts_spec_1.9.rb +24 -0
  164. data/lib/contracts-ruby3/spec/ruby_version_specific/contracts_spec_2.0.rb +55 -0
  165. data/lib/contracts-ruby3/spec/ruby_version_specific/contracts_spec_2.1.rb +63 -0
  166. data/lib/contracts-ruby3/spec/spec_helper.rb +102 -0
  167. data/lib/contracts-ruby3/spec/support.rb +10 -0
  168. data/lib/contracts-ruby3/spec/support_spec.rb +21 -0
  169. data/lib/contracts-ruby3/spec/validators_spec.rb +47 -0
  170. data/lib/entitlements/data/groups/calculated/yaml.rb +7 -1
  171. data/lib/entitlements/data/people/yaml.rb +9 -1
  172. data/lib/entitlements/extras/ldap_group/rules/ldap_group.rb +5 -1
  173. data/lib/entitlements/extras/orgchart/person_methods.rb +7 -1
  174. data/lib/entitlements.rb +13 -2
  175. data/lib/ruby_version_check.rb +17 -0
  176. 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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Contracts
4
+ VERSION = "0.17"
5
+ end