entitlements 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,47 @@
1
+ module Contracts
2
+ module MethodDecorators
3
+ def self.extended(klass)
4
+ Engine.apply(klass)
5
+ end
6
+
7
+ def inherited(subclass)
8
+ Engine.fetch_from(subclass).set_eigenclass_owner
9
+ super
10
+ end
11
+
12
+ def method_added(name)
13
+ MethodHandler.new(name, false, self).handle
14
+ super
15
+ end
16
+
17
+ def singleton_method_added(name)
18
+ MethodHandler.new(name, true, self).handle
19
+ super
20
+ end
21
+ end
22
+
23
+ class Decorator
24
+ # an attr_accessor for a class variable:
25
+ class << self; attr_accessor :decorators; end
26
+
27
+ def self.inherited(klass)
28
+ name = klass.name.gsub(/^./) { |m| m.downcase }
29
+
30
+ return if name =~ /^[^A-Za-z_]/ || name =~ /[^0-9A-Za-z_]/
31
+
32
+ # the file and line parameters set the text for error messages
33
+ # make a new method that is the name of your decorator.
34
+ # that method accepts random args and a block.
35
+ # inside, `decorate` is called with those params.
36
+ MethodDecorators.module_eval <<-ruby_eval, __FILE__, __LINE__ + 1
37
+ def #{klass}(*args, &blk)
38
+ ::Contracts::Engine.fetch_from(self).decorate(#{klass}, *args, &blk)
39
+ end
40
+ ruby_eval
41
+ end
42
+
43
+ def initialize(klass, method)
44
+ @method = method
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,136 @@
1
+ module Contracts
2
+ module Engine
3
+ # Contracts engine
4
+ class Base
5
+ # Enable contracts engine for klass
6
+ #
7
+ # @param [Class] klass - target class
8
+ def self.apply(klass)
9
+ Engine::Target.new(klass).apply
10
+ end
11
+
12
+ # Returns true if klass has contracts engine
13
+ #
14
+ # @param [Class] klass - target class
15
+ # @return [Bool]
16
+ def self.applied?(klass)
17
+ Engine::Target.new(klass).applied?
18
+ end
19
+
20
+ # Fetches contracts engine out of klass
21
+ #
22
+ # @param [Class] klass - target class
23
+ # @return [Engine::Base or Engine::Eigenclass]
24
+ def self.fetch_from(klass)
25
+ Engine::Target.new(klass).engine
26
+ end
27
+
28
+ # Creates new instance of contracts engine
29
+ #
30
+ # @param [Class] klass - class that owns this engine
31
+ def initialize(klass)
32
+ @klass = klass
33
+ end
34
+
35
+ # Adds provided decorator to the engine
36
+ # It validates that decorator can be added to this engine at the
37
+ # moment
38
+ #
39
+ # @param [Decorator:Class] decorator_class
40
+ # @param args - arguments for decorator
41
+ def decorate(decorator_class, *args)
42
+ validate!
43
+ decorators << [decorator_class, args]
44
+ end
45
+
46
+ # Sets eigenclass' owner to klass
47
+ def set_eigenclass_owner
48
+ eigenclass_engine.owner_class = klass
49
+ end
50
+
51
+ # Fetches all accumulated decorators (both this engine and
52
+ # corresponding eigenclass' engine)
53
+ # It clears all accumulated decorators
54
+ #
55
+ # @return [ArrayOf[Decorator]]
56
+ def all_decorators
57
+ pop_decorators + eigenclass_engine.all_decorators
58
+ end
59
+
60
+ # Fetches decorators of specified type for method with name
61
+ #
62
+ # @param [Or[:class_methods, :instance_methods]] type - method type
63
+ # @param [Symbol] name - method name
64
+ # @return [ArrayOf[Decorator]]
65
+ def decorated_methods_for(type, name)
66
+ Array(decorated_methods[type][name])
67
+ end
68
+
69
+ # Returns true if there are any decorated methods
70
+ #
71
+ # @return [Bool]
72
+ def decorated_methods?
73
+ !decorated_methods[:class_methods].empty? ||
74
+ !decorated_methods[:instance_methods].empty?
75
+ end
76
+
77
+ # Adds method decorator
78
+ #
79
+ # @param [Or[:class_methods, :instance_methods]] type - method type
80
+ # @param [Symbol] name - method name
81
+ # @param [Decorator] decorator - method decorator
82
+ def add_method_decorator(type, name, decorator)
83
+ decorated_methods[type][name] ||= []
84
+ decorated_methods[type][name] << decorator
85
+ end
86
+
87
+ # Returns nearest ancestor's engine that has decorated methods
88
+ #
89
+ # @return [Engine::Base or Engine::Eigenclass]
90
+ def nearest_decorated_ancestor
91
+ current = klass
92
+ current_engine = self
93
+ ancestors = current.ancestors[1..-1]
94
+
95
+ while current && current_engine && !current_engine.decorated_methods?
96
+ current = ancestors.shift
97
+ current_engine = Engine.fetch_from(current)
98
+ end
99
+
100
+ current_engine
101
+ end
102
+
103
+ private
104
+
105
+ attr_reader :klass
106
+
107
+ def decorated_methods
108
+ @_decorated_methods ||= { :class_methods => {}, :instance_methods => {} }
109
+ end
110
+
111
+ # No-op because it is safe to add decorators to normal classes
112
+ def validate!
113
+ end
114
+
115
+ def pop_decorators
116
+ decorators.tap { clear_decorators }
117
+ end
118
+
119
+ def eigenclass
120
+ Support.eigenclass_of(klass)
121
+ end
122
+
123
+ def eigenclass_engine
124
+ Eigenclass.lift(eigenclass, klass)
125
+ end
126
+
127
+ def decorators
128
+ @_decorators ||= []
129
+ end
130
+
131
+ def clear_decorators
132
+ @_decorators = []
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,50 @@
1
+ module Contracts
2
+ module Engine
3
+ # Special case of contracts engine for eigenclasses
4
+ # We don't care about eigenclass of eigenclass at this point
5
+ class Eigenclass < Base
6
+ # Class that owns this eigenclass
7
+ attr_accessor :owner_class
8
+
9
+ # Automatically enables eigenclass engine if it is not
10
+ # Returns its engine
11
+ # NOTE: Required by jruby in 1.9 mode. Otherwise inherited
12
+ # eigenclasses don't have their engines
13
+ #
14
+ # @param [Class] eigenclass - class in question
15
+ # @param [Class] owner - owner of eigenclass
16
+ # @return [Engine::Eigenclass]
17
+ def self.lift(eigenclass, owner)
18
+ return Engine.fetch_from(eigenclass) if Engine.applied?(eigenclass)
19
+
20
+ Target.new(eigenclass).apply(Eigenclass)
21
+ eigenclass.extend(MethodDecorators)
22
+ # FIXME; this should detect what user uses `include Contracts` or
23
+ # `include Contracts;;Core`
24
+ eigenclass.send(:include, Contracts)
25
+ Engine.fetch_from(owner).set_eigenclass_owner
26
+ Engine.fetch_from(eigenclass)
27
+ end
28
+
29
+ # No-op for eigenclasses
30
+ def set_eigenclass_owner
31
+ end
32
+
33
+ # Fetches just eigenclasses decorators
34
+ def all_decorators
35
+ pop_decorators
36
+ end
37
+
38
+ private
39
+
40
+ # Fails when contracts are not included in owner class
41
+ def validate!
42
+ fail ContractsNotIncluded unless owner?
43
+ end
44
+
45
+ def owner?
46
+ !!owner_class
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,70 @@
1
+ module Contracts
2
+ module Engine
3
+ # Represents class in question
4
+ class Target
5
+ # Creates new instance of Target
6
+ #
7
+ # @param [Class] target - class in question
8
+ def initialize(target)
9
+ @target = target
10
+ end
11
+
12
+ # Enable contracts engine for target
13
+ # - it is no-op if contracts engine is already enabled
14
+ # - it automatically enables contracts engine for its eigenclass
15
+ # - it sets owner class to target for its eigenclass
16
+ #
17
+ # @param [Engine::Base:Class] engine_class - type of engine to
18
+ # enable (Base or Eigenclass)
19
+ def apply(engine_class = Base)
20
+ return if applied?
21
+
22
+ apply_to_eigenclass
23
+
24
+ eigenclass.class_eval do
25
+ define_method(:__contracts_engine) do
26
+ @__contracts_engine ||= engine_class.new(self)
27
+ end
28
+ end
29
+
30
+ engine.set_eigenclass_owner
31
+ end
32
+
33
+ # Returns true if target has contracts engine already
34
+ #
35
+ # @return [Bool]
36
+ def applied?
37
+ target.respond_to?(:__contracts_engine)
38
+ end
39
+
40
+ # Returns contracts engine of target
41
+ #
42
+ # @return [Engine::Base or Engine::Eigenclass]
43
+ def engine
44
+ applied? && target.__contracts_engine
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :target
50
+
51
+ def apply_to_eigenclass
52
+ return unless meaningless_eigenclass?
53
+
54
+ self.class.new(eigenclass).apply(Eigenclass)
55
+ eigenclass.extend(MethodDecorators)
56
+ # FIXME; this should detect what user uses `include Contracts` or
57
+ # `include Contracts;;Core`
58
+ eigenclass.send(:include, Contracts)
59
+ end
60
+
61
+ def eigenclass
62
+ Support.eigenclass_of(target)
63
+ end
64
+
65
+ def meaningless_eigenclass?
66
+ !Support.eigenclass?(target)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,26 @@
1
+ require "contracts/engine/base"
2
+ require "contracts/engine/target"
3
+ require "contracts/engine/eigenclass"
4
+
5
+ require "forwardable"
6
+
7
+ module Contracts
8
+ # Engine facade, normally you shouldn't refer internals of Engine
9
+ # module directly.
10
+ module Engine
11
+ class << self
12
+ extend Forwardable
13
+
14
+ # .apply(klass) - enables contracts engine on klass
15
+ # .applied?(klass) - returns true if klass has contracts engine
16
+ # .fetch_from(klass) - returns contracts engine for klass
17
+ def_delegators :base_engine, :apply, :applied?, :fetch_from
18
+
19
+ private
20
+
21
+ def base_engine
22
+ Base
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,71 @@
1
+ # @private
2
+ # Base class for Contract errors
3
+ #
4
+ # If default failure callback is used it stores failure data
5
+ class ContractBaseError < ArgumentError
6
+ attr_reader :data
7
+
8
+ def initialize(message, data)
9
+ super(message)
10
+ @data = data
11
+ end
12
+
13
+ # Used to convert to simple ContractError from other contract errors
14
+ def to_contract_error
15
+ self
16
+ end
17
+ end
18
+
19
+ # Default contract error
20
+ #
21
+ # If default failure callback is used, users normally see only these contract errors
22
+ class ContractError < ContractBaseError
23
+ end
24
+
25
+ class ParamContractError < ContractError
26
+ end
27
+
28
+ class ReturnContractError < ContractError
29
+ end
30
+
31
+ # @private
32
+ # Special contract error used internally to detect pattern failure during pattern matching
33
+ class PatternMatchingError < ContractBaseError
34
+ # Used to convert to ContractError from PatternMatchingError
35
+ def to_contract_error
36
+ ContractError.new(to_s, data)
37
+ end
38
+ end
39
+
40
+ # Base invariant violation error
41
+ class InvariantError < StandardError
42
+ def to_contract_error
43
+ self
44
+ end
45
+ end
46
+
47
+ module Contracts
48
+ # Error issued when user haven't included Contracts in original class but used Contract definition in singleton class
49
+ #
50
+ # Provides useful description for user of the gem and an example of correct usage.
51
+ class ContractsNotIncluded < TypeError
52
+ DEFAULT_MESSAGE = %{In order to use contracts in singleton class, please include Contracts module in original class
53
+ Example:
54
+
55
+ ```ruby
56
+ class Example
57
+ include Contracts # this line is required
58
+ class << self
59
+ # you can use `Contract` definition here now
60
+ end
61
+ end
62
+ ```}
63
+
64
+ attr_reader :message
65
+ alias_method :to_s, :message
66
+
67
+ def initialize(message = DEFAULT_MESSAGE)
68
+ @message = message
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,136 @@
1
+ require "pp"
2
+
3
+ module Contracts
4
+ # A namespace for classes related to formatting.
5
+ module Formatters
6
+ # Used to format contracts for the `Expected:` field of error output.
7
+ class Expected
8
+ # @param full [Boolean] if false only unique `to_s` values will be output,
9
+ # non unique values become empty string.
10
+ def initialize(contract, full = true)
11
+ @contract, @full = contract, full
12
+ end
13
+
14
+ # Formats any type of Contract.
15
+ def contract(contract = @contract)
16
+ if contract.is_a?(Hash)
17
+ hash_contract(contract)
18
+ elsif contract.is_a?(Array)
19
+ array_contract(contract)
20
+ else
21
+ InspectWrapper.create(contract, @full)
22
+ end
23
+ end
24
+
25
+ # Formats Hash contracts.
26
+ def hash_contract(hash)
27
+ @full = true # Complex values output completely, overriding @full
28
+ hash.inject({}) do |repr, (k, v)|
29
+ repr.merge(k => InspectWrapper.create(contract(v), @full))
30
+ end
31
+ end
32
+
33
+ # Formats Array contracts.
34
+ def array_contract(array)
35
+ @full = true
36
+ array.map { |v| InspectWrapper.create(contract(v), @full) }
37
+ end
38
+ end
39
+
40
+ # A wrapper class to produce correct inspect behaviour for different
41
+ # contract values - constants, Class contracts, instance contracts etc.
42
+ module InspectWrapper
43
+ # InspectWrapper is a factory, will never be an instance
44
+ # @return [ClassInspectWrapper, ObjectInspectWrapper]
45
+ def self.create(value, full = true)
46
+ if value.class == Class
47
+ ClassInspectWrapper
48
+ else
49
+ ObjectInspectWrapper
50
+ end.new(value, full)
51
+ end
52
+
53
+ # @param full [Boolean] if false only unique `to_s` values will be output,
54
+ # non unique values become empty string.
55
+ def initialize(value, full)
56
+ @value, @full = value, full
57
+ end
58
+
59
+ # Inspect different types of contract values.
60
+ # Contracts module prefix will be removed from classes.
61
+ # Custom to_s messages will be wrapped in round brackets to differentiate
62
+ # from standard Strings.
63
+ # Primitive values e.g. 42, true, nil will be left alone.
64
+ def inspect
65
+ return "" unless full?
66
+ return @value.inspect if empty_val?
67
+ return @value.to_s if plain?
68
+ return delim(@value.to_s) if useful_to_s?
69
+ useful_inspect
70
+ end
71
+
72
+ def delim(value)
73
+ @full ? "(#{value})" : "#{value}"
74
+ end
75
+
76
+ # Eliminates eronious quotes in output that plain inspect includes.
77
+ def to_s
78
+ inspect
79
+ end
80
+
81
+ private
82
+
83
+ def empty_val?
84
+ @value.nil? || @value == ""
85
+ end
86
+
87
+ def full?
88
+ @full ||
89
+ @value.is_a?(Hash) || @value.is_a?(Array) ||
90
+ (!plain? && useful_to_s?)
91
+ end
92
+
93
+ def plain?
94
+ # Not a type of contract that can have a custom to_s defined
95
+ !@value.is_a?(Builtin::CallableClass) && @value.class != Class
96
+ end
97
+
98
+ def useful_to_s?
99
+ # Useless to_s value or no custom to_s behavious defined
100
+ !empty_to_s? && custom_to_s?
101
+ end
102
+
103
+ def empty_to_s?
104
+ @value.to_s.empty?
105
+ end
106
+
107
+ def strip_prefix(val)
108
+ val.gsub(/^Contracts::Builtin::/, "")
109
+ end
110
+ end
111
+
112
+ class ClassInspectWrapper
113
+ include InspectWrapper
114
+
115
+ def custom_to_s?
116
+ @value.to_s != @value.name
117
+ end
118
+
119
+ def useful_inspect
120
+ strip_prefix(empty_to_s? ? @value.name : @value.inspect)
121
+ end
122
+ end
123
+
124
+ class ObjectInspectWrapper
125
+ include InspectWrapper
126
+
127
+ def custom_to_s?
128
+ !@value.to_s.match(/#\<\w+:.+\>/)
129
+ end
130
+
131
+ def useful_inspect
132
+ strip_prefix(empty_to_s? ? @value.class.name : @value.inspect)
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,68 @@
1
+ module Contracts
2
+ module Invariants
3
+ def self.included(base)
4
+ common base
5
+ end
6
+
7
+ def self.extended(base)
8
+ common base
9
+ end
10
+
11
+ def self.common(base)
12
+ return if base.respond_to?(:Invariant)
13
+
14
+ base.extend(InvariantExtension)
15
+ end
16
+
17
+ def verify_invariants!(method)
18
+ return unless self.class.respond_to?(:invariants)
19
+
20
+ self.class.invariants.each do |invariant|
21
+ invariant.check_on(self, method)
22
+ end
23
+ end
24
+
25
+ module InvariantExtension
26
+ def invariant(name, &condition)
27
+ return if ENV["NO_CONTRACTS"]
28
+
29
+ invariants << Invariant.new(self, name, &condition)
30
+ end
31
+
32
+ def invariants
33
+ @invariants ||= []
34
+ end
35
+ end
36
+
37
+ class Invariant
38
+ def initialize(klass, name, &condition)
39
+ @klass, @name, @condition = klass, name, condition
40
+ end
41
+
42
+ def expected
43
+ "#{@name} condition to be true"
44
+ end
45
+
46
+ def check_on(target, method)
47
+ return if target.instance_eval(&@condition)
48
+
49
+ self.class.failure_callback(:expected => expected,
50
+ :actual => false,
51
+ :target => target,
52
+ :method => method)
53
+ end
54
+
55
+ def self.failure_callback(data)
56
+ fail InvariantError, failure_msg(data)
57
+ end
58
+
59
+ def self.failure_msg(data)
60
+ %{Invariant violation:
61
+ Expected: #{data[:expected]}
62
+ Actual: #{data[:actual]}
63
+ Value guarded in: #{data[:target].class}::#{Support.method_name(data[:method])}
64
+ At: #{Support.method_position(data[:method])}}
65
+ end
66
+ end
67
+ end
68
+ end