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