ree 1.0.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 (140) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +13 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +5 -0
  6. data/CODE_OF_CONDUCT.md +84 -0
  7. data/Gemfile +9 -0
  8. data/Gemfile.lock +41 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +474 -0
  11. data/Rakefile +8 -0
  12. data/bin/console +8 -0
  13. data/bin/setup +8 -0
  14. data/exe/ree +264 -0
  15. data/lib/ree/args.rb +34 -0
  16. data/lib/ree/bean_dsl.rb +24 -0
  17. data/lib/ree/cli/generate_package.rb +18 -0
  18. data/lib/ree/cli/generate_package_schema.rb +54 -0
  19. data/lib/ree/cli/generate_packages_schema.rb +17 -0
  20. data/lib/ree/cli/generate_template.rb +20 -0
  21. data/lib/ree/cli/init.rb +22 -0
  22. data/lib/ree/cli/spec_runner.rb +184 -0
  23. data/lib/ree/cli.rb +12 -0
  24. data/lib/ree/container.rb +67 -0
  25. data/lib/ree/contracts/arg_contracts/any.rb +15 -0
  26. data/lib/ree/contracts/arg_contracts/array_of.rb +52 -0
  27. data/lib/ree/contracts/arg_contracts/block.rb +15 -0
  28. data/lib/ree/contracts/arg_contracts/bool.rb +21 -0
  29. data/lib/ree/contracts/arg_contracts/eq.rb +28 -0
  30. data/lib/ree/contracts/arg_contracts/exactly.rb +28 -0
  31. data/lib/ree/contracts/arg_contracts/hash_of.rb +59 -0
  32. data/lib/ree/contracts/arg_contracts/ksplat.rb +141 -0
  33. data/lib/ree/contracts/arg_contracts/kwargs.rb +22 -0
  34. data/lib/ree/contracts/arg_contracts/nilor.rb +16 -0
  35. data/lib/ree/contracts/arg_contracts/none.rb +15 -0
  36. data/lib/ree/contracts/arg_contracts/optblock.rb +11 -0
  37. data/lib/ree/contracts/arg_contracts/or.rb +30 -0
  38. data/lib/ree/contracts/arg_contracts/range_of.rb +51 -0
  39. data/lib/ree/contracts/arg_contracts/set_of.rb +54 -0
  40. data/lib/ree/contracts/arg_contracts/splat.rb +297 -0
  41. data/lib/ree/contracts/arg_contracts/splat_of.rb +64 -0
  42. data/lib/ree/contracts/arg_contracts/squarable.rb +11 -0
  43. data/lib/ree/contracts/arg_contracts/subclass_of.rb +28 -0
  44. data/lib/ree/contracts/arg_contracts.rb +29 -0
  45. data/lib/ree/contracts/called_args_validator.rb +291 -0
  46. data/lib/ree/contracts/contract_definition.rb +142 -0
  47. data/lib/ree/contracts/contractable.rb +34 -0
  48. data/lib/ree/contracts/core.rb +17 -0
  49. data/lib/ree/contracts/engine.rb +71 -0
  50. data/lib/ree/contracts/engine_proxy.rb +13 -0
  51. data/lib/ree/contracts/errors/bad_contract_error.rb +4 -0
  52. data/lib/ree/contracts/errors/contract_error.rb +4 -0
  53. data/lib/ree/contracts/errors/error.rb +4 -0
  54. data/lib/ree/contracts/errors/return_contract_error.rb +4 -0
  55. data/lib/ree/contracts/method_decorator.rb +158 -0
  56. data/lib/ree/contracts/truncatable.rb +9 -0
  57. data/lib/ree/contracts/utils.rb +9 -0
  58. data/lib/ree/contracts/validators/array_validator.rb +51 -0
  59. data/lib/ree/contracts/validators/base_validator.rb +27 -0
  60. data/lib/ree/contracts/validators/class_validator.rb +17 -0
  61. data/lib/ree/contracts/validators/default_validator.rb +20 -0
  62. data/lib/ree/contracts/validators/hash_validator.rb +100 -0
  63. data/lib/ree/contracts/validators/proc_validator.rb +17 -0
  64. data/lib/ree/contracts/validators/range_validator.rb +17 -0
  65. data/lib/ree/contracts/validators/regexp_validator.rb +17 -0
  66. data/lib/ree/contracts/validators/valid_validator.rb +28 -0
  67. data/lib/ree/contracts/validators.rb +42 -0
  68. data/lib/ree/contracts.rb +45 -0
  69. data/lib/ree/core/link_validator.rb +42 -0
  70. data/lib/ree/core/object.rb +132 -0
  71. data/lib/ree/core/object_error.rb +9 -0
  72. data/lib/ree/core/object_link.rb +21 -0
  73. data/lib/ree/core/object_schema.rb +47 -0
  74. data/lib/ree/core/object_schema_builder.rb +110 -0
  75. data/lib/ree/core/package.rb +177 -0
  76. data/lib/ree/core/package_dep.rb +9 -0
  77. data/lib/ree/core/package_env_var.rb +12 -0
  78. data/lib/ree/core/package_loader.rb +95 -0
  79. data/lib/ree/core/package_schema.rb +27 -0
  80. data/lib/ree/core/package_schema_builder.rb +53 -0
  81. data/lib/ree/core/package_schema_loader.rb +170 -0
  82. data/lib/ree/core/packages_detector.rb +43 -0
  83. data/lib/ree/core/packages_schema.rb +19 -0
  84. data/lib/ree/core/packages_schema_builder.rb +50 -0
  85. data/lib/ree/core/packages_schema_loader.rb +95 -0
  86. data/lib/ree/core/packages_schema_locator.rb +27 -0
  87. data/lib/ree/core/packages_store.rb +32 -0
  88. data/lib/ree/core/path_helper.rb +104 -0
  89. data/lib/ree/dsl/build_package_dsl.rb +155 -0
  90. data/lib/ree/dsl/domain_error.rb +4 -0
  91. data/lib/ree/dsl/error_builder.rb +39 -0
  92. data/lib/ree/dsl/error_dsl.rb +27 -0
  93. data/lib/ree/dsl/import_dsl.rb +106 -0
  94. data/lib/ree/dsl/link_import_builder.rb +66 -0
  95. data/lib/ree/dsl/object_dsl.rb +319 -0
  96. data/lib/ree/dsl/object_hooks.rb +6 -0
  97. data/lib/ree/dsl/package_require.rb +44 -0
  98. data/lib/ree/error.rb +11 -0
  99. data/lib/ree/facades/packages_facade.rb +197 -0
  100. data/lib/ree/fn_dsl.rb +24 -0
  101. data/lib/ree/gen/init.rb +64 -0
  102. data/lib/ree/gen/package.rb +56 -0
  103. data/lib/ree/gen.rb +8 -0
  104. data/lib/ree/handlers/template_handler.rb +118 -0
  105. data/lib/ree/link_dsl.rb +175 -0
  106. data/lib/ree/object_compiler.rb +149 -0
  107. data/lib/ree/package_dsl.rb +34 -0
  108. data/lib/ree/rspec_link_dsl.rb +19 -0
  109. data/lib/ree/spec_runner/command_generator.rb +49 -0
  110. data/lib/ree/spec_runner/command_params.rb +9 -0
  111. data/lib/ree/spec_runner/runner.rb +200 -0
  112. data/lib/ree/spec_runner/spec_filename_matcher.rb +27 -0
  113. data/lib/ree/spec_runner/view.rb +30 -0
  114. data/lib/ree/spec_runner.rb +11 -0
  115. data/lib/ree/templates/init/.gitignore +1 -0
  116. data/lib/ree/templates/init/.irbrc +13 -0
  117. data/lib/ree/templates/init/.rspec +3 -0
  118. data/lib/ree/templates/init/.ruby-version +1 -0
  119. data/lib/ree/templates/init/Gemfile +7 -0
  120. data/lib/ree/templates/init/Packages.schema.json +1 -0
  121. data/lib/ree/templates/init/bin/console +5 -0
  122. data/lib/ree/templates/init/readme.md +2 -0
  123. data/lib/ree/templates/init/ree.setup.rb +21 -0
  124. data/lib/ree/templates/init/spec.init.rb +7 -0
  125. data/lib/ree/templates/package/.gitignore +0 -0
  126. data/lib/ree/templates/package/.rspec +2 -0
  127. data/lib/ree/templates/package/<%=package_subdir_name%>/<%=package_name%>/.gitkeep +0 -0
  128. data/lib/ree/templates/package/<%=package_subdir_name%>/<%=package_name%>.rb +15 -0
  129. data/lib/ree/templates/package/Package.schema.json +0 -0
  130. data/lib/ree/templates/package/bin/console +5 -0
  131. data/lib/ree/templates/package/spec/package_schema_spec.rb +14 -0
  132. data/lib/ree/templates/package/spec/spec_helper.rb +3 -0
  133. data/lib/ree/templates/template_detector.rb +35 -0
  134. data/lib/ree/templates/template_renderer.rb +55 -0
  135. data/lib/ree/utils/render_utils.rb +20 -0
  136. data/lib/ree/utils/string_utils.rb +29 -0
  137. data/lib/ree/version.rb +5 -0
  138. data/lib/ree.rb +279 -0
  139. data/sig/ree.rbs +4 -0
  140. metadata +199 -0
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal = true
2
+
3
+ require 'pathname'
4
+
5
+ class Ree::Container
6
+ class << self
7
+ SEMAPHORE = Mutex.new
8
+ private_constant :SEMAPHORE
9
+
10
+ # singleton
11
+ def instance
12
+ SEMAPHORE.synchronize do
13
+ @instance ||= begin
14
+ self.new
15
+ end
16
+ end
17
+
18
+ @instance
19
+ end
20
+ end
21
+
22
+ include Ree::Args
23
+
24
+ MOUNT_AS = [:fn, :object]
25
+
26
+ attr_reader :packages_facade
27
+
28
+ def initialize
29
+ @packages_facade = Ree::PackagesFacade.new
30
+ @object_compiler = Ree::ObjectCompiler.new(@packages_facade)
31
+ end
32
+
33
+ # @param [Ree::Package] package
34
+ # @param [Symbol] object_name
35
+ def compile(package, object_name)
36
+ compile_object("#{package.name}/#{object_name}")
37
+ end
38
+
39
+ # @param [String] name_with_package
40
+ # @return [Ree::Object]
41
+ def compile_object(name_with_package)
42
+ check_arg(name_with_package, :name_with_package, String)
43
+
44
+ list = name_with_package.to_s.split('/')
45
+
46
+ name = nil
47
+ package_name = nil
48
+
49
+ if list.size == 2
50
+ package_name = list.first.to_sym
51
+ name = list.last.to_sym
52
+ else
53
+ raise Ree::Error.new("'package/name' definition should be used to load object", :invalid_dsl_usage)
54
+ end
55
+
56
+ @packages_facade.get_loaded_package(package_name)
57
+ @packages_facade.load_package_object(package_name, name)
58
+
59
+ @object_compiler.call(package_name, name)
60
+ end
61
+
62
+ # @param [Symbol] package_name
63
+ # @return [Ree::Package]
64
+ def load_package(package_name)
65
+ @packages_facade.load_entire_package(package_name)
66
+ end
67
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class Any
6
+ def self.valid?(value)
7
+ true
8
+ end
9
+
10
+ def self.to_s
11
+ "Any"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class ArrayOf
6
+ extend Ree::Contracts::ArgContracts::Squarable
7
+ include Ree::Contracts::Truncatable
8
+
9
+ attr_reader :validator
10
+
11
+ def initialize(*contracts)
12
+ if contracts.size != 1
13
+ raise BadContractError, 'ArrayOf should accept exactly one contract'
14
+ end
15
+
16
+ @validator = Validators.fetch_for(contracts.first)
17
+ end
18
+
19
+ def valid?(value)
20
+ value.is_a?(Array) &&
21
+ value.all?(&validator.method(:call))
22
+ end
23
+
24
+ def to_s
25
+ "ArrayOf[#{validator.to_s}]"
26
+ end
27
+
28
+ def message(value, name, lvl = 1)
29
+ unless value.is_a?(Array)
30
+ return "expected Array, got #{value.class} => #{truncate(value.inspect)}"
31
+ end
32
+
33
+ errors = []
34
+ sps = " " * lvl
35
+
36
+ value.each_with_index do |val, idx|
37
+ next if validator.call(val)
38
+
39
+ msg = validator.message(val, "#{name}[#{idx}]", lvl + 1)
40
+ errors << "\n\t#{sps} - #{name}[#{idx}]: #{msg}"
41
+
42
+ if errors.size > 3
43
+ errors << "\n\t#{sps} - ..."
44
+ break
45
+ end
46
+ end
47
+
48
+ errors.join
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class Block
6
+ def self.valid?(*)
7
+ raise BadContractError, "#{name} contract is not allowed to use as argument contract"
8
+ end
9
+
10
+ def to_s
11
+ "Block"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class Bool
6
+ extend Ree::Contracts::Truncatable
7
+
8
+ def self.valid?(value)
9
+ value.is_a?(TrueClass) || value.is_a?(FalseClass)
10
+ end
11
+
12
+ def self.message(value, name, lvl = 1)
13
+ "expected Bool, got #{value.class} => #{truncate(value.inspect)}"
14
+ end
15
+
16
+ def self.to_s
17
+ "Bool"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class Eq
6
+ extend Ree::Contracts::ArgContracts::Squarable
7
+ include Ree::Contracts::Truncatable
8
+
9
+ attr_reader :contract
10
+
11
+ def initialize(contract)
12
+ @contract = contract
13
+ end
14
+
15
+ def valid?(value)
16
+ value.equal?(contract)
17
+ end
18
+
19
+ def to_s
20
+ "Eq[#{contract.inspect}]"
21
+ end
22
+
23
+ def message(value, name, lvl = 1)
24
+ "expected #{truncate(self.to_s, 30)}, got #{truncate(value.inspect)}"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class Exactly
6
+ extend Ree::Contracts::ArgContracts::Squarable
7
+ include Ree::Contracts::Truncatable
8
+
9
+ attr_reader :klass
10
+
11
+ def initialize(klass)
12
+ @klass = klass
13
+ end
14
+
15
+ def valid?(value)
16
+ value.class == klass
17
+ end
18
+
19
+ def to_s
20
+ "Exactly[#{klass.inspect}]"
21
+ end
22
+
23
+ def message(value, name, lvl = 1)
24
+ "expected #{truncate(self.to_s, 30)}, got #{truncate(value.class.inspect)}"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class HashOf
6
+ extend Ree::Contracts::ArgContracts::Squarable
7
+ include Ree::Contracts::Truncatable
8
+
9
+ attr_reader :key_validator, :value_validator
10
+
11
+ def initialize(*contracts)
12
+ if contracts.size != 2
13
+ raise BadContractError, 'HashOf should accept exactly two contracts'
14
+ end
15
+
16
+ @key_validator = Validators.fetch_for(contracts[0])
17
+ @value_validator = Validators.fetch_for(contracts[1])
18
+ end
19
+
20
+ def valid?(value)
21
+ value.is_a?(Hash) &&
22
+ value.each_key.all?(&key_validator.method(:call)) &&
23
+ value.each_value.all?(&value_validator.method(:call))
24
+ end
25
+
26
+ def to_s
27
+ "HashOf[#{key_validator.to_s}, #{value_validator.to_s}]"
28
+ end
29
+
30
+ def message(value, name, lvl = 1)
31
+ unless value.is_a?(Hash)
32
+ return "expected Hash, got #{value.class} => #{truncate(value.inspect)}"
33
+ end
34
+
35
+ errors = []
36
+ sps = " " * lvl
37
+
38
+ value.each do |key, val|
39
+ if errors.size > 4
40
+ errors << "\n\t#{sps} - ..."
41
+ break
42
+ end
43
+
44
+ unless key_validator.call(key)
45
+ msg = key_validator.message(key, "#{name}[#{key.inspect}]", lvl + 1)
46
+ errors << "\n\t#{sps} - invalid key #{key.inspect}, #{msg}"
47
+ end
48
+
49
+ unless value_validator.call(val)
50
+ msg = key_validator.message(val, "#{name}[#{key.inspect}]", lvl + 1)
51
+ errors << "\n\t#{sps} - invalid value for #{key.inspect} key, #{msg}"
52
+ end
53
+ end
54
+
55
+ errors.join
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module Ree::Contracts
6
+ module ArgContracts
7
+ class RestKeys; end
8
+
9
+ class Ksplat
10
+ include Ree::Contracts::Truncatable
11
+
12
+ attr_reader :validators, :rest_validator
13
+
14
+ def self.[](**contracts)
15
+ if contracts.empty?
16
+ raise BadContractError, 'Ksplat contract should accept at least one contract'
17
+ end
18
+
19
+ new(**contracts)
20
+ end
21
+
22
+ def initialize(**contracts)
23
+ @contracts = contracts
24
+ @opt_dict = Set.new
25
+
26
+ @validators = contracts
27
+ .transform_values { Validators.fetch_for(_1) }
28
+ .transform_keys { |key|
29
+ next key unless key.is_a?(String) || key.is_a?(Symbol)
30
+
31
+ key_str = key.to_s
32
+ next key unless key_str.end_with?('?') && key_str.length > 1
33
+
34
+ opt_key = key_str[0..-2]
35
+ opt_key = opt_key.to_sym if key.is_a? Symbol
36
+ @opt_dict << opt_key
37
+
38
+ opt_key
39
+ }
40
+
41
+ @rest_validator = @validators[RestKeys]
42
+
43
+ if @rest_validator
44
+ @validators.default = @rest_validator
45
+ end
46
+ end
47
+
48
+ def valid?(value)
49
+ return false unless value.is_a?(Hash)
50
+ return false if value.has_key?(RestKeys)
51
+
52
+ is_valid = value.all? do |key, v|
53
+ if @validators.has_key?(key)
54
+ @validators[key].call(v)
55
+ elsif @rest_validator
56
+ @rest_validator.call(v)
57
+ else
58
+ false
59
+ end
60
+ end
61
+
62
+ return false if !is_valid
63
+
64
+ return false if @validators.detect do |key, validator|
65
+ next if key == RestKeys || optional?(key)
66
+ next if value.has_key?(key)
67
+ true
68
+ end
69
+
70
+ true
71
+ end
72
+
73
+ def to_s
74
+ "Ksplat[#{validators.map { |k, v| "#{key_to_s(k)} => #{v.to_s}" }.join(', ')}]"
75
+ end
76
+
77
+ def message(value, name, lvl = 1)
78
+ unless value.is_a?(Hash)
79
+ return "expected Hash, got #{value.class} => #{truncate(value.inspect)}"
80
+ end
81
+
82
+ if value.has_key?(RestKeys)
83
+ return "RestKeys is a reserved key for Ksplat contract"
84
+ end
85
+
86
+ errors = []
87
+ sps = " " * lvl
88
+ all_keys = (@validators.keys - [RestKeys] + value.keys).uniq
89
+
90
+ all_keys.each do |key|
91
+ validator = @validators[key]
92
+
93
+ if !validator
94
+ errors << "\n\t#{sps} - #{name}[#{key.inspect}]: unexpected"
95
+ next
96
+ end
97
+
98
+ if !value.has_key?(key)
99
+ if !optional?(key)
100
+ errors << "\n\t#{sps} - #{name}[#{key.inspect}]: missing"
101
+ else
102
+ next
103
+ end
104
+ end
105
+
106
+ val = value[key]
107
+ next if validator.call(val)
108
+
109
+ msg = validator.message(val, "#{name}[#{key.inspect}]", lvl + 1)
110
+ errors << "\n\t#{sps} - #{name}[#{key.inspect}]: #{msg}"
111
+
112
+ if errors.size > 3
113
+ errors << "\n\t#{sps} - ..."
114
+ break
115
+ end
116
+ end
117
+
118
+ errors.join
119
+ end
120
+
121
+ private
122
+
123
+ def key_to_s(key)
124
+ k = if optional?(key)
125
+ v = "#{key}?"
126
+ key.is_a?(String) ? v : v.to_sym
127
+ elsif key == RestKeys
128
+ 'RestKeys'
129
+ else
130
+ key.to_s
131
+ end
132
+
133
+ k.inspect
134
+ end
135
+
136
+ def optional?(key)
137
+ @opt_dict.include?(key)
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class Kwargs < SimpleDelegator
6
+ def self.[](**contracts)
7
+ if contracts.empty?
8
+ raise BadContractError, 'Kwargs contract should accept at least one contract'
9
+ end
10
+
11
+ new(**contracts)
12
+ end
13
+
14
+ attr_reader :contracts
15
+
16
+ def initialize(**contracts)
17
+ @contracts = contracts
18
+ super(contracts)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class Nilor < Or
6
+ def initialize(*)
7
+ super
8
+ validators << Validators.fetch_for(nil)
9
+ end
10
+
11
+ def to_s
12
+ "Nilor[#{validators[0..-2].map(&:to_s).join(', ')}]"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class None
6
+ def self.valid?(*)
7
+ raise BadContractError, "None contract is not allowed to use as argument contract"
8
+ end
9
+
10
+ def self.to_s
11
+ "None"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class Optblock < Block
6
+ def to_s
7
+ "Optblock"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class Or
6
+ extend Ree::Contracts::ArgContracts::Squarable
7
+ include Ree::Contracts::Truncatable
8
+
9
+ attr_reader :validators
10
+
11
+ def initialize(*contracts)
12
+ @validators = contracts.map(&Validators.method(:fetch_for))
13
+ end
14
+
15
+ def to_s
16
+ "Or[#{validators.map(&:to_s).join(', ')}]"
17
+ end
18
+
19
+ def valid?(value)
20
+ validators.any? do |validator|
21
+ validator.call(value)
22
+ end
23
+ end
24
+
25
+ def message(value, name, lvl = 1)
26
+ "expected #{truncate(self.to_s, 30)}, got #{truncate(value.inspect)}"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::Contracts
4
+ module ArgContracts
5
+ class RangeOf
6
+ extend Ree::Contracts::ArgContracts::Squarable
7
+ include Ree::Contracts::Truncatable
8
+
9
+ attr_reader :validator
10
+
11
+ def initialize(*contracts)
12
+ if contracts.size != 1
13
+ raise BadContractError, 'RangeOf should accept exactly one contract'
14
+ end
15
+
16
+ @validator = Validators.fetch_for(contracts.first)
17
+ end
18
+
19
+ def valid?(value)
20
+ value.is_a?(Range) &&
21
+ validator.call(value.begin) &&
22
+ validator.call(value.end)
23
+ end
24
+
25
+ def to_s
26
+ "RangeOf[#{validator.to_s}]"
27
+ end
28
+
29
+ def message(value, name, lvl = 1)
30
+ unless value.is_a?(Range)
31
+ return "expected Range, got #{value.class} => #{truncate(value.inspect)}"
32
+ end
33
+
34
+ errors = []
35
+ sps = " " * lvl
36
+
37
+ unless validator.call(value.begin)
38
+ msg = validator.message(value.begin, "#{name}.begin", lvl + 1)
39
+ errors << "\n\t#{sps} - #{name}.begin: #{msg}"
40
+ end
41
+
42
+ unless validator.call(value.end)
43
+ msg = validator.message(value.end, "#{name}.end", lvl + 1)
44
+ errors << "\n\t#{sps} - #{name}.end: #{msg}"
45
+ end
46
+
47
+ errors.join
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module Ree::Contracts
6
+ module ArgContracts
7
+ class SetOf
8
+ extend Ree::Contracts::ArgContracts::Squarable
9
+ include Ree::Contracts::Truncatable
10
+
11
+ attr_reader :validator
12
+
13
+ def initialize(*contracts)
14
+ if contracts.size != 1
15
+ raise BadContractError, 'SetOf should accept exactly one contract'
16
+ end
17
+
18
+ @validator = Validators.fetch_for(contracts.first)
19
+ end
20
+
21
+ def valid?(value)
22
+ value.is_a?(Set) &&
23
+ value.all?(&validator.method(:call))
24
+ end
25
+
26
+ def to_s
27
+ "SetOf[#{validator.to_s}]"
28
+ end
29
+
30
+ def message(value, name, lvl = 1)
31
+ unless value.is_a?(Set)
32
+ return "expected Set, got #{value.class} => #{truncate(value.inspect)}"
33
+ end
34
+
35
+ errors = []
36
+ sps = " " * lvl
37
+
38
+ value.each_with_index do |val, idx|
39
+ next if validator.call(val)
40
+
41
+ msg = validator.message(val, "#{name}[#{idx}]", lvl + 1)
42
+ errors << "\n\t#{sps} - #{name}[#{idx}]: #{msg}"
43
+
44
+ if errors.size > 3
45
+ errors << "\n\t#{sps} - ..."
46
+ break
47
+ end
48
+ end
49
+
50
+ errors.join
51
+ end
52
+ end
53
+ end
54
+ end