data_model 0.3.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b19c7734dd0562c0ef848b80626af66d08fbb9899b4110bf489ddb5d8920bb96
4
- data.tar.gz: 9292e0267765d965788f54a37d6846a123acd045506f7451412be625193fb3e6
3
+ metadata.gz: 77b4364d2dc4198264ace712d5cb9f74a52120a6e878345f122091b4f10ec355
4
+ data.tar.gz: b886dbf46aa42f0bbfc8364cce01f959f1e4fc37849f83f3b3d0cf88521e46fa
5
5
  SHA512:
6
- metadata.gz: f151b11149b2c97e93773f5857a16914ee2c85a3b83074106832ae426f43467533b79423ef5b943e6bca9b109a9db699ff06f454dda3c434f3844969c26330a2
7
- data.tar.gz: 235d0c7e9f26539e997f394a449e4f0a6662cce55563aa49e29552289087bd619730d992e2f691733a730a3a4b04ae7577c84a5b5f80441b3ebcd91e633aeedf
6
+ metadata.gz: 57c5dbc66e6d50aae767bb6f8eb7d192bbbef1912737675bc2ff63efb3ada68f06bee62a4f291d3b47e962d59d104f103d47ac2ce76ff6e6723e3c4cee090167
7
+ data.tar.gz: db075c6e83530263ecd90396c989f2fffd52a2b434031102501f85464f1e8253dd5fd85ae99b4c1a82622d6f0e21b1fd5808f19809575e6db264cb70c91dbc3c
data/.rubocop.yml CHANGED
@@ -9,5 +9,8 @@ AllCops:
9
9
  Style/GlobalStdStream:
10
10
  Enabled: false
11
11
 
12
+ Metrics/ClassLength:
13
+ Enabled: false
14
+
12
15
  Lint/BooleanSymbol:
13
16
  Enabled: false
@@ -0,0 +1,2 @@
1
+ /.*
2
+ !/.gitignore
@@ -0,0 +1,37 @@
1
+ (provide "ruby" "3.2.0")
2
+
3
+ ; remove any existing ruby paths and environment variables
4
+ (when-let ((ruby-root (env/get "RUBY_ROOT")))
5
+ (env/remove-from-pathlist "PATH" (path-concat ruby-root "bin"))
6
+ (when-let ((gem-root (env/get "GEM_ROOT")))
7
+ (env/remove-from-pathlist "PATH" (path-concat gem-root "bin")))
8
+ (when-let ((gem-home (env/get "GEM_HOME")))
9
+ (env/remove-from-pathlist "PATH" (path-concat gem-home "bin"))))
10
+
11
+ (env/set "GEM_PATH" ())
12
+ (env/set "GEM_HOME" ())
13
+ (env/set "RUBYOPT" ())
14
+
15
+ ; set base ruby environment
16
+ (let ((version "3.2.2")
17
+ (gem-version "3.2.0")
18
+ (ruby-home (path-concat (env/get "HOME") ".rubies" (concat "ruby-" version))))
19
+ (do
20
+ (env/set "RUBY_ROOT" ruby-home)
21
+ (env/prepend-to-pathlist "PATH" (path-concat ruby-home "bin"))
22
+ (env/set "RUBY_ENGINE" "ruby")
23
+ (env/set "RUBY_VERSION" version)
24
+ (env/set "GEM_ROOT" (path-concat ruby-home "lib" "ruby" "gems" gem-version))))
25
+
26
+ ; set gem environment
27
+ (when-let ((gem-root (env/get "GEM_ROOT")))
28
+ (env/prepend-to-pathlist "GEM_PATH" gem-root)
29
+ (env/prepend-to-pathlist "PATH" (path-concat gem-root "bin")))
30
+
31
+ ; handle local gem home
32
+ (let ((gem-home
33
+ (path-concat (env/get "HOME") ".gem" (env/get "RUBY_ENGINE") (env/get "RUBY_VERSION"))))
34
+ (do
35
+ (env/set "GEM_HOME" gem-home)
36
+ (env/prepend-to-pathlist "GEM_PATH" gem-home)
37
+ (env/prepend-to-pathlist "PATH" (path-concat gem-home "bin"))))
data/Gemfile.lock CHANGED
@@ -68,6 +68,7 @@ GEM
68
68
  rubocop-ast (1.28.0)
69
69
  parser (>= 3.2.1.0)
70
70
  ruby-progressbar (1.13.0)
71
+ rufo (0.16.1)
71
72
  shellany (0.0.1)
72
73
  sorbet (0.5.10795)
73
74
  sorbet-static (= 0.5.10795)
@@ -110,6 +111,7 @@ DEPENDENCIES
110
111
  minitest
111
112
  rake
112
113
  rubocop
114
+ rufo
113
115
  sorbet-runtime
114
116
  tapioca
115
117
 
data/data_model.gemspec CHANGED
@@ -43,6 +43,7 @@ Gem::Specification.new do |spec|
43
43
  spec.add_development_dependency "minitest"
44
44
  spec.add_development_dependency "rake"
45
45
  spec.add_development_dependency "rubocop"
46
+ spec.add_development_dependency "rufo"
46
47
  spec.add_development_dependency "sorbet-runtime"
47
48
  spec.add_development_dependency "tapioca"
48
49
 
@@ -4,6 +4,7 @@ module DataModel
4
4
  # Error is a class that holds errors.
5
5
  class Error
6
6
  extend T::Sig
7
+ include Errors
7
8
 
8
9
  TErrorList = T.type_alias { T::Array[TError] }
9
10
  TErrorMap = T.type_alias { T::Hash[Symbol, TErrorList] }
@@ -86,22 +87,9 @@ module DataModel
86
87
  end
87
88
  end
88
89
 
89
- sig { params(blk: T.proc.params(context: Object, type: Symbol).returns(Object)).void }
90
- def transform_context(&blk)
91
- for error in @base
92
- key, context = error
93
- error[1] = blk.call(context, key)
94
- end
95
- end
96
-
97
- sig { params(blk: T.proc.params(context: Object, type: Symbol).returns(Object)).void }
98
- def transform_child_context(&blk)
99
- for error_list in @children.values
100
- for error in error_list
101
- key, context = error
102
- error[1] = blk.call(context, key)
103
- end
104
- end
90
+ sig { params(registry: Registry).returns(T::Hash[Symbol, T::Array[String]]) }
91
+ def to_messages(registry: Registry.instance)
92
+ return registry.error_messages(self)
105
93
  end
106
94
  end
107
95
  end
@@ -5,6 +5,7 @@ module DataModel
5
5
  module Errors
6
6
  include Kernel
7
7
  extend T::Sig
8
+ extend self
8
9
 
9
10
  TTemporal = T.type_alias { T.any(::Date, ::Time, ::DateTime) }
10
11
 
@@ -156,141 +157,80 @@ module DataModel
156
157
  "value #{val} does not match format #{format}"
157
158
  end
158
159
 
159
- ## API
160
- # TODO: split this file
160
+ # Builders
161
161
 
162
162
  TErrorMessageBuilder = T.type_alias { T.proc.params(ctx: T.untyped).returns(String) }
163
-
164
- # Register a custom error message for use with custom errors
165
- sig { params(type: Symbol, block: TErrorMessageBuilder).void }
166
- def register_error_message(type, &block)
167
- error_message_builders[type] = block
168
- end
169
-
170
163
  TErrorMessages = T.type_alias { T::Hash[Symbol, TErrorMessageBuilder] }
171
164
  TClassValueCtx = T.type_alias { [T.class_of(Object), Object] }
172
165
  TClassCtx = T.type_alias { T.class_of(Object) }
173
166
  TSetCtx = T.type_alias { T::Array[Symbol] }
174
167
  TWithinCtx = T.type_alias { [Numeric, Numeric] }
175
- TWithinTemporalCtx = T.type_alias { [TTemporal, TTemporal] }
168
+ TWithinTemporalCtx = T.type_alias { [Errors::TTemporal, Errors::TTemporal] }
176
169
  TFormatCtx = T.type_alias { [Object, String] }
177
170
 
178
171
  # Get the error message builders
179
172
  sig { returns(TErrorMessages) }
180
- def error_message_builders
181
- if @error_messages.nil?
182
- @error_messages ||= T.let({}, T.nilable(TErrorMessages))
183
-
184
- # wire up defaults
185
-
186
- register_error_message(:type) do |ctx|
173
+ def self.error_messages
174
+ return {
175
+ type: lambda do |ctx|
187
176
  cls, val = T.let(ctx, TClassValueCtx)
188
177
  type_error_message(cls, val)
189
- end
178
+ end,
190
179
 
191
- register_error_message(:coerce) do |ctx|
180
+ coerce: lambda do |ctx|
192
181
  cls, val = T.let(ctx, TClassValueCtx)
193
- coerce_error_message(cls, val)
194
- end
182
+ type_error_message(cls, val)
183
+ end,
195
184
 
196
- register_error_message(:missing) do |ctx|
185
+ missing: lambda do |ctx|
197
186
  cls = T.let(ctx, TClassCtx)
198
187
  missing_error_message(cls)
199
- end
188
+ end,
200
189
 
201
- register_error_message(:inclusion) do |ctx|
190
+ inclusion: lambda do |ctx|
202
191
  set = T.let(ctx, TSetCtx)
203
192
  inclusion_error_message(set)
204
- end
193
+ end,
205
194
 
206
- register_error_message(:exclusion) do |ctx|
195
+ exclusion: lambda do |ctx|
207
196
  set = T.let(ctx, TSetCtx)
208
197
  exclusion_error_message(set)
209
- end
198
+ end,
210
199
 
211
- register_error_message(:extra_keys) do |ctx|
200
+ extra_keys: lambda do |ctx|
212
201
  set = T.let(ctx, TSetCtx)
213
202
  extra_keys_error_message(set)
214
- end
203
+ end,
215
204
 
216
- register_error_message(:min) do |ctx|
205
+ min: lambda do |ctx|
217
206
  min, val = T.let(ctx, TWithinCtx)
218
207
  min_error_message(min, val)
219
- end
208
+ end,
220
209
 
221
- register_error_message(:max) do |ctx|
210
+ max: lambda do |ctx|
222
211
  max, val = T.let(ctx, TWithinCtx)
223
212
  max_error_message(max, val)
224
- end
213
+ end,
225
214
 
226
- register_error_message(:earliest) do |ctx|
215
+ earliest: lambda do |ctx|
227
216
  earliest, val = T.let(ctx, TWithinTemporalCtx)
228
217
  early_error_message(earliest, val)
229
- end
218
+ end,
230
219
 
231
- register_error_message(:latest) do |ctx|
220
+ latest: lambda do |ctx|
232
221
  latest, val = T.let(ctx, TWithinTemporalCtx)
233
222
  late_error_message(latest, val)
234
- end
223
+ end,
235
224
 
236
- register_error_message(:blank) do
225
+ blank: lambda do
237
226
  blank_error_message
238
- end
227
+ end,
239
228
 
240
- register_error_message(:format) do |ctx|
229
+ format: lambda do |ctx|
241
230
  format, val = T.let(ctx, TFormatCtx)
242
231
  format_error_message(format, val)
243
232
  end
244
- end
245
-
246
- @error_messages
247
- end
248
-
249
- # Build the error message for a given error
250
- sig { params(error: TError).returns(String) }
251
- def error_message(error)
252
- type = T.let(error[0], Symbol)
253
- ctx = T.let(error[1], T.untyped)
254
-
255
- builder = error_message_builders[type]
256
-
257
- if builder.nil?
258
- raise "no error message builder for #{type}"
259
- end
260
-
261
- builder.call(ctx)
262
- end
263
-
264
- # TODO: separate builders from other use cases for this mixin
265
- # Build error messages from error object
266
- sig { params(error: Error).returns(T::Hash[Symbol, T::Array[String]]) }
267
- def error_messages(error)
268
- error.to_h.transform_values do |error_list|
269
- error_list.map { |e| error_message(e) }
270
- end
271
- end
272
-
273
- sig { params(error: Error, from: T.class_of(Object), to: T.class_of(Object)).void }
274
- def set_error_class(error, from, to)
275
- error.transform_context do |ctx, type|
276
- case type
277
- when :type, :coerce
278
- cls, val = T.cast(ctx, TClassValueCtx)
279
- if cls == from
280
- [to, val]
281
- else
282
- [cls, val]
283
- end
284
- when :missing
285
- if ctx == from
286
- [to, val]
287
- else
288
- [cls, val]
289
- end
290
- else
291
- [cls, val]
292
- end
293
- end
233
+ }
294
234
  end
295
235
  end
296
236
  end
@@ -0,0 +1,114 @@
1
+ # typed: strict
2
+
3
+ module DataModel
4
+ # Registry allows for different type implementations to be used by the scanner.
5
+ # It also acts as an error message registry, mostly for pragmatic reasons.
6
+ class Registry
7
+ extend T::Sig
8
+
9
+ # Default types that will be used if alternative type map is not given
10
+ sig { returns(TTypeMap) }
11
+ def self.default_types
12
+ Builtin.types
13
+ end
14
+
15
+ sig { returns(Errors::TErrorMessages) }
16
+ def self.default_error_messages
17
+ Errors.error_messages
18
+ end
19
+
20
+ # Singleton instance that will be used globally unless instances given
21
+ sig { params(types: TTypeMap, errors: Errors::TErrorMessages).returns(Registry) }
22
+ def self.instance(types: default_types, errors: default_error_messages)
23
+ @instance ||= T.let(new(types:, errors:), T.nilable(Registry))
24
+ end
25
+
26
+ # Register a type on the global instance
27
+ sig { params(name: Symbol, type: T.class_of(Type)).void }
28
+ def self.register(name, type)
29
+ instance.register(name, type)
30
+ end
31
+
32
+ # Instanciate a new type registry. Default errors will always be used, but additional
33
+ # errors can be registered.
34
+ sig { params(types: TTypeMap, errors: Errors::TErrorMessages).void }
35
+ def initialize(types: self.class.default_types, errors: self.class.default_error_messages)
36
+ @error_messages = T.let(nil, T.nilable(Errors::TErrorMessages))
37
+ if errors
38
+ errors.each { |type, builder| register_error_message(type, &builder) }
39
+ end
40
+
41
+ @types = T.let({}, TTypeMap)
42
+ types.each { |(name, type)| register(name, type) }
43
+ end
44
+
45
+ # Register a type on this instance
46
+ sig { params(name: Symbol, type: T.class_of(Type)).void }
47
+ def register(name, type)
48
+ @types[name] = type
49
+ end
50
+
51
+ # Check if a type is registered
52
+ sig { params(name: Symbol).returns(T::Boolean) }
53
+ def type?(name)
54
+ @types.key?(name)
55
+ end
56
+
57
+ # Access and configure registered type
58
+ sig { params(name: Symbol, args: Type::TArguments, params: T.nilable(T::Array[Object])).returns(Type) }
59
+ def type(name, args: {}, params: nil)
60
+ if !type?(name)
61
+ raise "#{name} is not registered as a type"
62
+ end
63
+
64
+ t = @types.fetch(name).new(args, registry: self)
65
+
66
+ if params
67
+ t.configure(params)
68
+ end
69
+
70
+ return t
71
+ end
72
+
73
+ ## API
74
+
75
+ # Register a custom error message for use with custom errors
76
+ sig { params(type: Symbol, block: Errors::TErrorMessageBuilder).void }
77
+ def register_error_message(type, &block)
78
+ error_message_builders[type] = block
79
+ end
80
+
81
+ # Get the error message builders
82
+ sig { returns(Errors::TErrorMessages) }
83
+ def error_message_builders
84
+ if @error_messages.nil?
85
+ @error_messages ||= T.let({}, T.nilable(Errors::TErrorMessages))
86
+ end
87
+
88
+ @error_messages
89
+ end
90
+
91
+ # Build the error message for a given error
92
+ sig { params(error: TError).returns(String) }
93
+ def error_message(error)
94
+ type = T.let(error[0], Symbol)
95
+ ctx = T.let(error[1], T.untyped)
96
+
97
+ builder = error_message_builders[type]
98
+
99
+ if builder.nil?
100
+ raise "no error message builder for #{type}"
101
+ end
102
+
103
+ builder.call(ctx)
104
+ end
105
+
106
+ # Build error messages from error object
107
+ sig { params(error: Error).returns(T::Hash[Symbol, T::Array[String]]) }
108
+ def error_messages(error)
109
+ error.to_h.transform_values do |error_list|
110
+ error_list.map { |e| error_message(e) }
111
+ end
112
+ end
113
+ end
114
+ end
@@ -32,8 +32,8 @@ module DataModel
32
32
 
33
33
  # Scan a schema, which is defined as a data structure, into a struct that is easier to work with.
34
34
  # "Syntax" validations will be enforced at this level.
35
- sig { params(schema: TSchema, registry: DataModel::TypeRegistry).returns(Node) }
36
- def scan(schema, registry = TypeRegistry.instance)
35
+ sig { params(schema: TSchema, registry: DataModel::Registry).returns(Node) }
36
+ def scan(schema, registry = Registry.instance)
37
37
  # state:
38
38
  # nil (start) -> :type (we have a type) -> :args (we have arguments)
39
39
  scanned = Node.new
@@ -12,8 +12,8 @@ module DataModel
12
12
  TTypeParams = T.type_alias { T::Array[Object] }
13
13
  TTypeResult = T.type_alias { [Object, Error] }
14
14
 
15
- sig { params(args: TArguments, registry: TypeRegistry).void }
16
- def initialize(args, registry: TypeRegistry.instance)
15
+ sig { params(args: TArguments, registry: Registry).void }
16
+ def initialize(args, registry: Registry.instance)
17
17
  @type_args = args
18
18
  @type_registry = registry
19
19
  end
@@ -1,5 +1,5 @@
1
1
  # typed: strict
2
2
 
3
3
  module DataModel
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/data_model.rb CHANGED
@@ -27,8 +27,8 @@ module DataModel
27
27
  TTypeMap = T.type_alias { T::Hash[Symbol, T.class_of(Type)] }
28
28
 
29
29
  # Scan a schema and create a data model, which is a configured type.
30
- sig { params(schema: TSchema, registry: TypeRegistry).returns(Model) }
31
- def define(schema, registry: TypeRegistry.instance)
30
+ sig { params(schema: TSchema, registry: Registry).returns(Model) }
31
+ def define(schema, registry: Registry.instance)
32
32
  scanned = Scanner.scan(schema, registry)
33
33
 
34
34
  type = registry.type(
@@ -44,6 +44,6 @@ module DataModel
44
44
 
45
45
  sig { params(name: Symbol, type: T.class_of(Type)).void }
46
46
  def register_global_type(name, type)
47
- TypeRegistry.register(name, type)
47
+ Registry.register(name, type)
48
48
  end
49
49
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: data_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Briggs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-11 00:00:00.000000000 Z
11
+ date: 2023-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sorbet
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rufo
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: sorbet-runtime
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -146,6 +160,8 @@ files:
146
160
  - ".editorconfig"
147
161
  - ".rubocop.yml"
148
162
  - ".ruby-version"
163
+ - ".shadowenv.d/.gitignore"
164
+ - ".shadowenv.d/550-ruby.lisp"
149
165
  - Gemfile
150
166
  - Gemfile.lock
151
167
  - Guardfile
@@ -179,11 +195,11 @@ files:
179
195
  - lib/data_model/fixtures/time.rb
180
196
  - lib/data_model/logging.rb
181
197
  - lib/data_model/model.rb
198
+ - lib/data_model/registry.rb
182
199
  - lib/data_model/scanner.rb
183
200
  - lib/data_model/testing.rb
184
201
  - lib/data_model/testing/minitest.rb
185
202
  - lib/data_model/type.rb
186
- - lib/data_model/type_registry.rb
187
203
  - lib/data_model/version.rb
188
204
  - sorbet/config
189
205
  - sorbet/rbi/annotations/rainbow.rbi
@@ -217,7 +233,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
217
233
  - !ruby/object:Gem::Version
218
234
  version: '0'
219
235
  requirements: []
220
- rubygems_version: 3.4.12
236
+ rubygems_version: 3.4.10
221
237
  signing_key:
222
238
  specification_version: 4
223
239
  summary: Define a model for your data
@@ -1,68 +0,0 @@
1
- # typed: strict
2
-
3
- module DataModel
4
- # TypeRegistry allows for different type implementations to be used by the scanner.
5
- # It also acts as an error message registry, mostly for pragmatic reasons.
6
- class TypeRegistry
7
- include Errors
8
- extend T::Sig
9
-
10
- # Default types that will be used if alternative type map is not given
11
- sig { returns(TTypeMap) }
12
- def self.default_types
13
- Builtin.types
14
- end
15
-
16
- # Singleton instance that will be used globally unless instances given
17
- sig { params(types: TTypeMap, errors: T.nilable(TErrorMessages)).returns(TypeRegistry) }
18
- def self.instance(types: default_types, errors: nil)
19
- @instance ||= T.let(new(types:, errors:), T.nilable(TypeRegistry))
20
- end
21
-
22
- # Register a type on the global instance
23
- sig { params(name: Symbol, type: T.class_of(Type)).void }
24
- def self.register(name, type)
25
- instance.register(name, type)
26
- end
27
-
28
- # Instanciate a new type registry. Default errors will always be used, but additional
29
- # errors can be registered.
30
- sig { params(types: TTypeMap, errors: T.nilable(TErrorMessages)).void }
31
- def initialize(types: self.class.default_types, errors: nil)
32
- if errors
33
- errors.each { |type, builder| register_error_message(type, &builder) }
34
- end
35
-
36
- @types = T.let({}, TTypeMap)
37
- types.each { |(name, type)| register(name, type) }
38
- end
39
-
40
- # Register a type on this instance
41
- sig { params(name: Symbol, type: T.class_of(Type)).void }
42
- def register(name, type)
43
- @types[name] = type
44
- end
45
-
46
- # Check if a type is registered
47
- sig { params(name: Symbol).returns(T::Boolean) }
48
- def type?(name)
49
- @types.key?(name)
50
- end
51
-
52
- # Access and configure registered type
53
- sig { params(name: Symbol, args: Type::TArguments, params: T.nilable(T::Array[Object])).returns(Type) }
54
- def type(name, args: {}, params: nil)
55
- if !type?(name)
56
- raise "#{name} is not registered as a type"
57
- end
58
-
59
- t = @types.fetch(name).new(args, registry: self)
60
-
61
- if params
62
- t.configure(params)
63
- end
64
-
65
- return t
66
- end
67
- end
68
- end