data_model 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +6 -2
  3. data/.rubocop.yml +11 -2
  4. data/.ruby-version +2 -0
  5. data/Gemfile.lock +91 -54
  6. data/Guardfile +20 -0
  7. data/Rakefile +32 -0
  8. data/data_model.gemspec +52 -0
  9. data/lib/data_model/boolean.rb +7 -0
  10. data/lib/data_model/builtin/array.rb +73 -0
  11. data/lib/data_model/builtin/big_decimal.rb +64 -0
  12. data/lib/data_model/builtin/boolean.rb +37 -0
  13. data/lib/data_model/builtin/date.rb +60 -0
  14. data/lib/data_model/builtin/float.rb +64 -0
  15. data/lib/data_model/builtin/hash.rb +119 -0
  16. data/lib/data_model/builtin/integer.rb +64 -0
  17. data/lib/data_model/builtin/string.rb +88 -0
  18. data/lib/data_model/builtin/symbol.rb +64 -0
  19. data/lib/data_model/builtin/time.rb +60 -0
  20. data/lib/data_model/builtin.rb +23 -0
  21. data/lib/data_model/error.rb +107 -0
  22. data/lib/data_model/errors.rb +296 -0
  23. data/lib/data_model/fixtures/array.rb +61 -0
  24. data/lib/data_model/fixtures/big_decimal.rb +55 -0
  25. data/lib/data_model/fixtures/boolean.rb +35 -0
  26. data/lib/data_model/fixtures/date.rb +53 -0
  27. data/lib/data_model/fixtures/example.rb +29 -0
  28. data/lib/data_model/fixtures/float.rb +53 -0
  29. data/lib/data_model/fixtures/hash.rb +66 -0
  30. data/lib/data_model/fixtures/integer.rb +53 -0
  31. data/lib/data_model/fixtures/string.rb +110 -0
  32. data/lib/data_model/fixtures/symbol.rb +56 -0
  33. data/lib/data_model/fixtures/time.rb +53 -0
  34. data/lib/data_model/logging.rb +23 -0
  35. data/lib/data_model/model.rb +21 -44
  36. data/lib/data_model/scanner.rb +92 -56
  37. data/lib/data_model/testing/minitest.rb +79 -0
  38. data/lib/data_model/testing.rb +6 -0
  39. data/lib/data_model/type.rb +41 -39
  40. data/lib/data_model/type_registry.rb +68 -0
  41. data/lib/data_model/version.rb +3 -1
  42. data/lib/data_model.rb +32 -16
  43. data/sorbet/config +4 -0
  44. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  45. data/sorbet/rbi/gems/minitest@5.18.0.rbi +1491 -0
  46. data/sorbet/rbi/gems/zeitwerk.rbi +196 -0
  47. data/sorbet/rbi/gems/zeitwerk@2.6.7.rbi +966 -0
  48. data/sorbet/rbi/todo.rbi +5 -0
  49. data/sorbet/tapioca/config.yml +13 -0
  50. data/sorbet/tapioca/require.rb +4 -0
  51. metadata +139 -17
  52. data/config/sus.rb +0 -2
  53. data/fixtures/schema.rb +0 -14
  54. data/lib/data_model/registry.rb +0 -44
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6f87b58a7e7706f284041ccc8479520af309c3e632ef89311be431a11112273
4
- data.tar.gz: e945d11a1ac73459be8b1c067eb8bc40cafe7f43a665cb069fecf880d413d91d
3
+ metadata.gz: c62c4d806d3e7bbbac5e4031d91b6f681dcb2697fd09e6a81c90e1b3cdc95ad6
4
+ data.tar.gz: 6bde7c362323265251688a38946ea992451fdd9d1c57838d6dfaa563a49b814d
5
5
  SHA512:
6
- metadata.gz: f6470deed88de3b60a5aad2e5926724f4d3255567d0668016ff6c13e1a82608ea71dc8b82ae82a4b0993771c222ad3a9353236243e6c0e8f8c4c0423614e8c71
7
- data.tar.gz: 82434cccbdb292e7e416e46eb1eb353193ff39329008a107b2837d97b2c907228c79c62f3be16a2f5966c437c777372f2c08708e0acf5b688fb2f22232565dae
6
+ metadata.gz: 7ed6672730afd6792a13c753ad5bc96694d5dc8982d3411b3d0af3f25e5682096f6a08c939837f473983f264960e1047d076c3304ca98618c020796ab312449f
7
+ data.tar.gz: 2a849997505c5d60c04fc48a51bd5049eb4ee2b90a2d5490929261fb1c03fed818e87834bdc800d748e71d5fd7fd454a19c2664e3c28044c486884149452feed
data/.editorconfig CHANGED
@@ -2,9 +2,13 @@ root = true
2
2
 
3
3
  [*]
4
4
  indent_style = tab
5
- indent_size = 2
5
+ indent_size = tab
6
+ insert_final_newline = true
7
+ tab_width = 4
8
+ end_of_line = lf
9
+ charset = utf-8
10
+ trim_trailing_whitespace = true
6
11
 
7
12
  [*.yml]
8
13
  indent_style = space
9
14
  indent_size = 2
10
-
data/.rubocop.yml CHANGED
@@ -1,4 +1,13 @@
1
1
  inherit_from: "https://raw.githubusercontent.com/mbriggs/configs/main/dotfiles/rubocop.yml"
2
2
 
3
- Style/SymbolArray:
4
- EnforcedStyle: brackets
3
+ AllCops:
4
+ SuggestExtensions: false
5
+ Exclude:
6
+ - 'bin/**/*'
7
+ - 'sorbet/**/*'
8
+
9
+ Style/GlobalStdStream:
10
+ Enabled: false
11
+
12
+ Lint/BooleanSymbol:
13
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1,2 @@
1
+ system
2
+
data/Gemfile.lock CHANGED
@@ -1,80 +1,117 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- data_model (0.0.1)
5
- zeitwerk (~> 2.6)
4
+ data_model (0.2.0)
5
+ sorbet
6
+ zeitwerk
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
10
11
  ast (2.4.2)
11
- backport (1.2.0)
12
- benchmark (0.2.0)
12
+ coderay (1.1.3)
13
13
  diff-lcs (1.5.0)
14
- e2mmap (0.1.0)
15
- jaro_winkler (1.5.4)
16
- json (2.6.2)
17
- kramdown (2.4.0)
18
- rexml
19
- kramdown-parser-gfm (1.1.0)
20
- kramdown (~> 2.0)
21
- mini_portile2 (2.8.0)
22
- nokogiri (1.13.8)
23
- mini_portile2 (~> 2.8.0)
24
- racc (~> 1.4)
25
- parallel (1.22.1)
26
- parser (3.1.2.1)
14
+ ffi (1.15.5)
15
+ formatador (1.1.0)
16
+ guard (2.18.0)
17
+ formatador (>= 0.2.4)
18
+ listen (>= 2.7, < 4.0)
19
+ lumberjack (>= 1.0.12, < 2.0)
20
+ nenv (~> 0.1)
21
+ notiffany (~> 0.0)
22
+ pry (>= 0.13.0)
23
+ shellany (~> 0.0)
24
+ thor (>= 0.18.1)
25
+ guard-rake (1.0.0)
26
+ guard
27
+ rake
28
+ json (2.6.3)
29
+ listen (3.8.0)
30
+ rb-fsevent (~> 0.10, >= 0.10.3)
31
+ rb-inotify (~> 0.9, >= 0.9.10)
32
+ lumberjack (1.2.8)
33
+ method_source (1.0.0)
34
+ minitest (5.18.0)
35
+ nenv (0.3.0)
36
+ netrc (0.11.0)
37
+ notiffany (0.1.3)
38
+ nenv (~> 0.1)
39
+ shellany (~> 0.0)
40
+ parallel (1.23.0)
41
+ parser (3.2.2.1)
27
42
  ast (~> 2.4.1)
28
- racc (1.6.0)
43
+ pry (0.14.2)
44
+ coderay (~> 1.1)
45
+ method_source (~> 1.0)
29
46
  rainbow (3.1.1)
30
- regexp_parser (2.6.0)
31
- reverse_markdown (2.1.1)
32
- nokogiri
47
+ rake (13.0.6)
48
+ rb-fsevent (0.11.2)
49
+ rb-inotify (0.10.1)
50
+ ffi (~> 1.0)
51
+ rbi (0.0.16)
52
+ ast
53
+ parser (>= 2.6.4.0)
54
+ sorbet-runtime (>= 0.5.9204)
55
+ unparser
56
+ regexp_parser (2.8.0)
33
57
  rexml (3.2.5)
34
- rubocop (1.36.0)
58
+ rubocop (1.50.2)
35
59
  json (~> 2.3)
36
60
  parallel (~> 1.10)
37
- parser (>= 3.1.2.1)
61
+ parser (>= 3.2.0.0)
38
62
  rainbow (>= 2.2.2, < 4.0)
39
63
  regexp_parser (>= 1.8, < 3.0)
40
64
  rexml (>= 3.2.5, < 4.0)
41
- rubocop-ast (>= 1.20.1, < 2.0)
65
+ rubocop-ast (>= 1.28.0, < 2.0)
42
66
  ruby-progressbar (~> 1.7)
43
- unicode-display_width (>= 1.4.0, < 3.0)
44
- rubocop-ast (1.21.0)
45
- parser (>= 3.1.1.0)
46
- ruby-progressbar (1.11.0)
47
- solargraph (0.47.2)
48
- backport (~> 1.2)
49
- benchmark
50
- bundler (>= 1.17.2)
51
- diff-lcs (~> 1.4)
52
- e2mmap
53
- jaro_winkler (~> 1.5)
54
- kramdown (~> 2.3)
55
- kramdown-parser-gfm (~> 1.1)
56
- parser (~> 3.0)
57
- reverse_markdown (>= 1.0.5, < 3)
58
- rubocop (>= 0.52)
59
- thor (~> 1.0)
60
- tilt (~> 2.0)
61
- yard (~> 0.9, >= 0.9.24)
62
- sus (0.14.0)
67
+ unicode-display_width (>= 2.4.0, < 3.0)
68
+ rubocop-ast (1.28.0)
69
+ parser (>= 3.2.1.0)
70
+ ruby-progressbar (1.13.0)
71
+ shellany (0.0.1)
72
+ sorbet (0.5.10795)
73
+ sorbet-static (= 0.5.10795)
74
+ sorbet-runtime (0.5.10795)
75
+ sorbet-static (0.5.10795-universal-darwin-22)
76
+ sorbet-static-and-runtime (0.5.10795)
77
+ sorbet (= 0.5.10795)
78
+ sorbet-runtime (= 0.5.10795)
79
+ spoom (1.2.1)
80
+ sorbet (>= 0.5.10187)
81
+ sorbet-runtime (>= 0.5.9204)
82
+ thor (>= 0.19.2)
83
+ tapioca (0.11.5)
84
+ bundler (>= 2.2.25)
85
+ netrc (>= 0.11.0)
86
+ parallel (>= 1.21.0)
87
+ rbi (~> 0.0.0, >= 0.0.16)
88
+ sorbet-static-and-runtime (>= 0.5.10187)
89
+ spoom (~> 1.2.0, >= 1.2.0)
90
+ thor (>= 1.2.0)
91
+ yard-sorbet
63
92
  thor (1.2.1)
64
- tilt (2.0.11)
65
- unicode-display_width (2.3.0)
66
- webrick (1.7.0)
67
- yard (0.9.28)
68
- webrick (~> 1.7.0)
69
- zeitwerk (2.6.1)
93
+ unicode-display_width (2.4.2)
94
+ unparser (0.6.7)
95
+ diff-lcs (~> 1.3)
96
+ parser (>= 3.2.0)
97
+ yard (0.9.34)
98
+ yard-sorbet (0.8.1)
99
+ sorbet-runtime (>= 0.5)
100
+ yard (>= 0.9)
101
+ zeitwerk (2.6.7)
70
102
 
71
103
  PLATFORMS
72
- ruby
104
+ arm64-darwin-22
73
105
 
74
106
  DEPENDENCIES
75
107
  data_model!
76
- solargraph
77
- sus (~> 0.14)
108
+ guard
109
+ guard-rake
110
+ minitest
111
+ rake
112
+ rubocop
113
+ sorbet-runtime
114
+ tapioca
78
115
 
79
116
  BUNDLED WITH
80
- 2.3.7
117
+ 2.4.12
data/Guardfile ADDED
@@ -0,0 +1,20 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ guard "rake", task: "test" do
19
+ watch(/.rb$/)
20
+ end
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ # typed: false
2
+
3
+ require "minitest/test_task"
4
+
5
+ Minitest::TestTask.create # named test, sensible defaults
6
+
7
+ # or more explicitly:
8
+
9
+ Minitest::TestTask.create(:test) do |t|
10
+ t.libs << "test"
11
+ t.libs << "lib"
12
+ t.warning = false
13
+ t.test_globs = ["test/**/*_test.rb"]
14
+ end
15
+
16
+ task default: :test
17
+
18
+ require "rubocop/rake_task"
19
+ RuboCop::RakeTask.new
20
+
21
+ require "bundler"
22
+ namespace :gem do
23
+ Bundler::GemHelper.install_tasks
24
+ end
25
+
26
+ task "test:watch" do
27
+ sh "bundle exec guard"
28
+ end
29
+
30
+ task :tc do
31
+ sh "bundle exec srb tc"
32
+ end
@@ -0,0 +1,52 @@
1
+ require_relative "lib/data_model/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "data_model"
5
+ spec.version = DataModel::VERSION
6
+ spec.authors = ["Matt Briggs"]
7
+ spec.email = ["matt@mattbriggs.net"]
8
+
9
+ spec.summary = "Define a model for your data"
10
+ spec.description = "A framework for describing and validating data."
11
+ spec.homepage = "https://github.com/mbriggs/data_model"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = ">= 3.1"
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = "https://github.com/mbriggs/data_model"
17
+ spec.metadata[
18
+ "changelog_uri"
19
+ ] = "https://github.com/mbriggs/data_model/releases"
20
+ spec.metadata["funding_uri"] = "https://github.com/sponsors/mbriggs"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files =
25
+ Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0")
27
+ .reject do |f|
28
+ (f == __FILE__) ||
29
+ f.match(
30
+ %r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)},
31
+ )
32
+ end
33
+ end
34
+ spec.bindir = "exe"
35
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
36
+ spec.require_paths = ["lib"]
37
+
38
+ spec.add_dependency "sorbet"
39
+ spec.add_dependency "zeitwerk"
40
+
41
+ spec.add_development_dependency "guard"
42
+ spec.add_development_dependency "guard-rake"
43
+ spec.add_development_dependency "minitest"
44
+ spec.add_development_dependency "rake"
45
+ spec.add_development_dependency "rubocop"
46
+ spec.add_development_dependency "sorbet-runtime"
47
+ spec.add_development_dependency "tapioca"
48
+
49
+ # For more information and examples about making a new gem, check out our
50
+ # guide at: https://bundler.io/guides/creating_gem.html
51
+ spec.metadata["rubygems_mfa_required"] = "true"
52
+ end
@@ -0,0 +1,7 @@
1
+ # typed: strict
2
+
3
+ module DataModel
4
+ # fills in for lack of boolean type in ruby
5
+ class Boolean
6
+ end
7
+ end
@@ -0,0 +1,73 @@
1
+ # typed: strict
2
+
3
+ module DataModel
4
+ class Builtin::Array < Type
5
+ include Errors
6
+
7
+ class Arguments < T::Struct
8
+ prop :optional, T::Boolean, default: false
9
+ prop :wrap_single_value, T::Boolean, default: false
10
+ prop :min, T.nilable(T.any(Integer, Float, Rational, BigDecimal)), default: nil
11
+ prop :max, T.nilable(T.any(Integer, Float, Rational, BigDecimal)), default: nil
12
+ prop :length, T.nilable(T.any(Integer, Float, Rational, BigDecimal)), default: nil
13
+ end
14
+
15
+ # support either :string shorthand or [:string, {optional: true}]
16
+ sig { override.params(params: T::Array[Object]).void }
17
+ def configure(params)
18
+ if params.first.is_a?(Array)
19
+ params = params.first
20
+ end
21
+
22
+ params = T.cast(params, TSchema)
23
+ node = Scanner.scan(params)
24
+ type = instantiate(node.type, args: node.args, params: node.params)
25
+
26
+ @child_type = T.let(type, T.nilable(Type))
27
+ end
28
+
29
+ sig { returns(Type) }
30
+ def child_type
31
+ if @child_type.nil?
32
+ raise "children not configured"
33
+ end
34
+
35
+ return @child_type
36
+ end
37
+
38
+ sig { override.params(val: Object, coerce: T::Boolean).returns(TTypeResult) }
39
+ def read(val, coerce: false)
40
+ args = Arguments.new(type_args)
41
+ err = Error.new
42
+
43
+ if val.nil? && !args.optional
44
+ err.add(missing_error(Array))
45
+ return [val, err]
46
+ end
47
+
48
+ if coerce && args.wrap_single_value
49
+ val = Array(val)
50
+ end
51
+
52
+ if !val.is_a?(Array)
53
+ err.add(type_error(Array, val))
54
+ return [val, err]
55
+ end
56
+
57
+ coerced = []
58
+
59
+ for i in 0...val.length
60
+ child_val = T.let(val[i], Object)
61
+ child, child_err = child_type.read(child_val, coerce:)
62
+ if child_err
63
+ err.merge_child(i.to_s.to_sym, child_err)
64
+ end
65
+ coerced << child
66
+ end
67
+
68
+ result = coerce ? coerced : val
69
+
70
+ return [result, err]
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,64 @@
1
+ # typed: strict
2
+
3
+ module DataModel
4
+ class Builtin::BigDecimal < Type
5
+ include Errors
6
+
7
+ class Arguments < T::Struct
8
+ prop :optional, T::Boolean, default: false
9
+ prop :min, T.nilable(T.any(Integer, Float, Rational, BigDecimal)), default: nil
10
+ prop :max, T.nilable(T.any(Integer, Float, Rational, BigDecimal)), default: nil
11
+ end
12
+
13
+ sig { override.params(val: Object, coerce: T::Boolean).returns(TTypeResult) }
14
+ def read(val, coerce: false)
15
+ err = Error.new
16
+ args = Arguments.new(type_args)
17
+
18
+ if args.optional && val.nil?
19
+ return [val, err]
20
+ end
21
+
22
+ if !args.optional && val.nil?
23
+ err.add(missing_error(BigDecimal))
24
+ return [val, err]
25
+ end
26
+
27
+ if !val.is_a?(BigDecimal) && !coerce
28
+ err.add(type_error(BigDecimal, val))
29
+ return [val, err]
30
+ end
31
+
32
+ if !val.is_a?(BigDecimal) && coerce
33
+ if val.is_a?(String) || val.is_a?(Numeric)
34
+ val = BigDecimal(T.unsafe(val))
35
+ elsif val.respond_to?(:to_d)
36
+ val = T.cast(T.unsafe(val).to_d, BigDecimal)
37
+ end
38
+
39
+ if !val.is_a?(BigDecimal)
40
+ err.add(coerce_error(BigDecimal, val))
41
+ return [val, err]
42
+ end
43
+ end
44
+
45
+ val = T.cast(val, BigDecimal)
46
+
47
+ min = args.min
48
+ if min && val <= min
49
+ err.add(min_error(min, val))
50
+
51
+ return [val, err]
52
+ end
53
+
54
+ max = args.max
55
+ if max && val <= max
56
+ err.add(max_error(max, val))
57
+
58
+ return [val, err]
59
+ end
60
+
61
+ [val, err]
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,37 @@
1
+ # typed: strict
2
+
3
+ module DataModel
4
+ class Builtin::Boolean < Type
5
+ include Errors
6
+
7
+ class Arguments < T::Struct
8
+ prop :optional, T::Boolean, default: false
9
+ end
10
+
11
+ sig { override.params(val: Object, coerce: T::Boolean).returns(TTypeResult) }
12
+ def read(val, coerce: false)
13
+ err = Error.new
14
+
15
+ if val.nil?
16
+ err.add(missing_error(DataModel::Boolean))
17
+ return [val, err]
18
+ end
19
+
20
+ if coerce
21
+ case val
22
+ when "true"
23
+ val = true
24
+ when "false"
25
+ val = false
26
+ end
27
+ end
28
+
29
+ if !val.is_a?(TrueClass) && !val.is_a?(FalseClass)
30
+ err.add(type_error(Boolean, val))
31
+ return [val, err]
32
+ end
33
+
34
+ return [val, err]
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,60 @@
1
+ # typed: strict
2
+
3
+ module DataModel
4
+ class Builtin::Date < Type
5
+ include Errors
6
+
7
+ class Arguments < T::Struct
8
+ prop :optional, T::Boolean, default: false
9
+ prop :earliest, T.nilable(::Date), default: nil
10
+ prop :latest, T.nilable(::Date), default: nil
11
+ end
12
+
13
+ sig { override.params(val: Object, coerce: T::Boolean).returns(TTypeResult) }
14
+ def read(val, coerce: false)
15
+ args = Arguments.new(type_args)
16
+ err = Error.new
17
+
18
+ # missing, but allowed, don't do any more checks
19
+ if val.nil? && args.optional
20
+ return [val, err]
21
+ end
22
+
23
+ # missing, but not allowed, don't do any more checks
24
+ if val.nil?
25
+ err.add(missing_error(Date))
26
+ return [val, err]
27
+ end
28
+
29
+ # coercion is enabled, and the value is a string, try to parse it
30
+ if val.is_a?(String) && coerce
31
+ begin
32
+ val = Date.parse(val)
33
+ rescue ArgumentError
34
+ err.add(type_error(Date, val))
35
+ return [val, err]
36
+ end
37
+ end
38
+
39
+ # not a date, don't do any more checks
40
+ if !val.is_a?(Date)
41
+ err.add(type_error(Date, val))
42
+ return [val, err]
43
+ end
44
+
45
+ # date is before the earliest point allowed
46
+ if args.earliest && (val < args.earliest)
47
+ error = earliest_error(T.must(args.earliest), val)
48
+ err.add(error)
49
+ end
50
+
51
+ # date is after the latest point allowed
52
+ if args.latest && (val > args.latest)
53
+ error = latest_error(T.must(args.latest), val)
54
+ err.add(error)
55
+ end
56
+
57
+ return [val, err]
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,64 @@
1
+ # typed: strict
2
+
3
+ module DataModel
4
+ class Builtin::Float < Type
5
+ include Errors
6
+
7
+ class Arguments < T::Struct
8
+ prop :optional, T::Boolean, default: false
9
+ prop :min, T.nilable(T.any(Integer, Float, Rational, BigDecimal)), default: nil
10
+ prop :max, T.nilable(T.any(Integer, Float, Rational, BigDecimal)), default: nil
11
+ end
12
+
13
+ sig { override.params(val: Object, coerce: T::Boolean).returns(TTypeResult) }
14
+ def read(val, coerce: false)
15
+ err = Error.new
16
+ args = Arguments.new(type_args)
17
+
18
+ if args.optional && val.nil?
19
+ return [val, err]
20
+ end
21
+
22
+ if !args.optional && val.nil?
23
+ err.add(missing_error(Float))
24
+ return [val, err]
25
+ end
26
+
27
+ if !val.is_a?(Float) && !coerce
28
+ err.add(type_error(Float, val))
29
+ return [val, err]
30
+ end
31
+
32
+ if !val.is_a?(Float) && coerce
33
+ if val.is_a?(String) || val.is_a?(Numeric)
34
+ val = Float(val)
35
+ elsif val.respond_to?(:to_f)
36
+ val = T.cast(T.unsafe(val).to_f, Float)
37
+ end
38
+
39
+ if !val.is_a?(Float)
40
+ err.add(coerce_error(Float, val))
41
+ return [val, err]
42
+ end
43
+ end
44
+
45
+ val = T.cast(val, Float)
46
+
47
+ min = args.min
48
+ if min && val <= min
49
+ err.add(min_error(min, val))
50
+
51
+ return [val, err]
52
+ end
53
+
54
+ max = args.max
55
+ if max && val <= max
56
+ err.add(max_error(max, val))
57
+
58
+ return [val, err]
59
+ end
60
+
61
+ [val, err]
62
+ end
63
+ end
64
+ end