data_model 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c62c4d806d3e7bbbac5e4031d91b6f681dcb2697fd09e6a81c90e1b3cdc95ad6
4
- data.tar.gz: 6bde7c362323265251688a38946ea992451fdd9d1c57838d6dfaa563a49b814d
3
+ metadata.gz: 77b4364d2dc4198264ace712d5cb9f74a52120a6e878345f122091b4f10ec355
4
+ data.tar.gz: b886dbf46aa42f0bbfc8364cce01f959f1e4fc37849f83f3b3d0cf88521e46fa
5
5
  SHA512:
6
- metadata.gz: 7ed6672730afd6792a13c753ad5bc96694d5dc8982d3411b3d0af3f25e5682096f6a08c939837f473983f264960e1047d076c3304ca98618c020796ab312449f
7
- data.tar.gz: 2a849997505c5d60c04fc48a51bd5049eb4ee2b90a2d5490929261fb1c03fed818e87834bdc800d748e71d5fd7fd454a19c2664e3c28044c486884149452feed
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- data_model (0.2.0)
4
+ data_model (0.3.0)
5
5
  sorbet
6
6
  zeitwerk
7
7
 
@@ -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.2.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(
@@ -41,4 +41,9 @@ module DataModel
41
41
 
42
42
  return model
43
43
  end
44
+
45
+ sig { params(name: Symbol, type: T.class_of(Type)).void }
46
+ def register_global_type(name, type)
47
+ Registry.register(name, type)
48
+ end
44
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.2.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