zero_ruby 0.1.0.alpha2 → 0.1.0.alpha4

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +124 -70
  3. data/lib/zero_ruby/configuration.rb +0 -5
  4. data/lib/zero_ruby/error_formatter.rb +171 -0
  5. data/lib/zero_ruby/errors.rb +10 -1
  6. data/lib/zero_ruby/input_object.rb +56 -93
  7. data/lib/zero_ruby/lmid_stores/active_record_store.rb +0 -1
  8. data/lib/zero_ruby/mutation.rb +201 -77
  9. data/lib/zero_ruby/push_processor.rb +108 -34
  10. data/lib/zero_ruby/schema.rb +38 -14
  11. data/lib/zero_ruby/type_names.rb +13 -8
  12. data/lib/zero_ruby/types.rb +54 -0
  13. data/lib/zero_ruby/typescript_generator.rb +126 -58
  14. data/lib/zero_ruby/version.rb +1 -1
  15. data/lib/zero_ruby.rb +11 -34
  16. metadata +46 -20
  17. data/lib/zero_ruby/argument.rb +0 -75
  18. data/lib/zero_ruby/types/base_type.rb +0 -54
  19. data/lib/zero_ruby/types/big_int.rb +0 -32
  20. data/lib/zero_ruby/types/boolean.rb +0 -30
  21. data/lib/zero_ruby/types/float.rb +0 -31
  22. data/lib/zero_ruby/types/id.rb +0 -33
  23. data/lib/zero_ruby/types/integer.rb +0 -31
  24. data/lib/zero_ruby/types/iso8601_date.rb +0 -43
  25. data/lib/zero_ruby/types/iso8601_date_time.rb +0 -43
  26. data/lib/zero_ruby/types/string.rb +0 -20
  27. data/lib/zero_ruby/validator.rb +0 -69
  28. data/lib/zero_ruby/validators/allow_blank_validator.rb +0 -31
  29. data/lib/zero_ruby/validators/allow_null_validator.rb +0 -26
  30. data/lib/zero_ruby/validators/exclusion_validator.rb +0 -29
  31. data/lib/zero_ruby/validators/format_validator.rb +0 -35
  32. data/lib/zero_ruby/validators/inclusion_validator.rb +0 -30
  33. data/lib/zero_ruby/validators/length_validator.rb +0 -42
  34. data/lib/zero_ruby/validators/numericality_validator.rb +0 -63
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry-types"
4
+
5
+ module ZeroRuby
6
+ # Type definitions using dry-types.
7
+ #
8
+ # When inheriting from ZeroRuby::Mutation or ZeroRuby::InputObject, types are
9
+ # available via ZeroRuby::TypeNames which is automatically included:
10
+ # - Direct: ID, Boolean, ISO8601Date, ISO8601DateTime
11
+ # - Via Types: Types::String, Types::Integer, Types::Float
12
+ #
13
+ # @example Basic usage
14
+ # class MyMutation < ZeroRuby::Mutation
15
+ # argument :id, ID
16
+ # argument :name, Types::String
17
+ # argument :count, Types::Integer.optional
18
+ # argument :active, Boolean.default(false)
19
+ # end
20
+ #
21
+ # @example With constraints
22
+ # class MyMutation < ZeroRuby::Mutation
23
+ # argument :title, Types::String.constrained(min_size: 1, max_size: 200)
24
+ # argument :count, Types::Integer.constrained(gt: 0)
25
+ # argument :status, Types::String.constrained(included_in: %w[draft published])
26
+ # end
27
+ module Types
28
+ include Dry.Types()
29
+
30
+ # Params types for JSON input
31
+ # These handle string coercions common in form/JSON data
32
+
33
+ # String type (passes through strings, coerces nil)
34
+ String = Params::String
35
+
36
+ # Coerces string numbers to integers (e.g., "42" -> 42)
37
+ Integer = Params::Integer
38
+
39
+ # Coerces string numbers to floats (e.g., "3.14" -> 3.14)
40
+ Float = Params::Float
41
+
42
+ # Coerces string booleans (e.g., "true" -> true, "false" -> false)
43
+ Boolean = Params::Bool
44
+
45
+ # Non-empty string ID type
46
+ ID = Params::String.constrained(filled: true)
47
+
48
+ # ISO8601 date string -> Date object
49
+ ISO8601Date = Params::Date
50
+
51
+ # ISO8601 datetime string -> DateTime object
52
+ ISO8601DateTime = Params::DateTime
53
+ end
54
+ end
@@ -9,15 +9,16 @@ module ZeroRuby
9
9
  # TypeScriptGenerator.new(ZeroSchema).generate
10
10
  # # => "// Auto-generated by zero-ruby - do not edit\n\nexport interface ..."
11
11
  class TypeScriptGenerator
12
- TYPE_MAP = {
13
- "ZeroRuby::Types::String" => "string",
14
- "ZeroRuby::Types::Integer" => "number",
15
- "ZeroRuby::Types::Float" => "number",
16
- "ZeroRuby::Types::Boolean" => "boolean",
17
- "ZeroRuby::Types::ID" => "string",
18
- "ZeroRuby::Types::BigInt" => "number",
19
- "ZeroRuby::Types::ISO8601Date" => "string",
20
- "ZeroRuby::Types::ISO8601DateTime" => "string"
12
+ # Map Ruby primitives to TypeScript types
13
+ PRIMITIVE_MAP = {
14
+ String => "string",
15
+ Integer => "number",
16
+ Float => "number",
17
+ TrueClass => "boolean",
18
+ FalseClass => "boolean",
19
+ Date => "string",
20
+ DateTime => "string",
21
+ Time => "string"
21
22
  }.freeze
22
23
 
23
24
  def initialize(schema)
@@ -32,7 +33,6 @@ module ZeroRuby
32
33
 
33
34
  parts = [
34
35
  generate_header,
35
- generate_scalars,
36
36
  generate_input_objects,
37
37
  generate_mutation_args,
38
38
  generate_mutation_map
@@ -46,20 +46,6 @@ module ZeroRuby
46
46
  def generate_header
47
47
  <<~TS
48
48
  // Auto-generated by zero-ruby - do not edit
49
- // Generated at: #{Time.now.utc.iso8601}
50
- TS
51
- end
52
-
53
- def generate_scalars
54
- <<~TS
55
-
56
- /** Scalar type mappings */
57
- export type Scalars = {
58
- String: string;
59
- Integer: number;
60
- Float: number;
61
- Boolean: boolean;
62
- };
63
49
  TS
64
50
  end
65
51
 
@@ -70,13 +56,19 @@ module ZeroRuby
70
56
  end
71
57
 
72
58
  def collect_input_objects_from_arguments(arguments)
73
- arguments.each do |_name, arg|
74
- if input_object_type?(arg.type)
75
- type_name = extract_type_name(arg.type)
59
+ arguments.each do |_name, config|
60
+ type = config[:type]
61
+ if input_object_type?(type)
62
+ type_name = extract_type_name(type)
76
63
  unless @input_objects.key?(type_name)
77
- @input_objects[type_name] = arg.type
78
- # Recursively collect nested InputObjects
79
- collect_input_objects_from_arguments(arg.type.arguments)
64
+ @input_objects[type_name] = type
65
+ # Recursively collect nested InputObjects from Dry::Struct schema
66
+ if type.respond_to?(:schema)
67
+ nested_args = type.schema.keys.each_with_object({}) do |key, hash|
68
+ hash[key.name] = { type: key.type, name: key.name }
69
+ end
70
+ collect_input_objects_from_arguments(nested_args)
71
+ end
80
72
  end
81
73
  end
82
74
  end
@@ -86,36 +78,57 @@ module ZeroRuby
86
78
  return nil if @input_objects.empty?
87
79
 
88
80
  interfaces = @input_objects.map do |name, klass|
89
- generate_interface(name, klass.arguments)
81
+ generate_input_object_interface(name, klass)
90
82
  end
91
83
 
92
84
  "\n" + interfaces.join("\n\n")
93
85
  end
94
86
 
87
+ def generate_input_object_interface(name, klass)
88
+ return empty_interface(name) unless klass.respond_to?(:schema)
89
+
90
+ keys = klass.schema.keys
91
+ return empty_interface(name) if keys.empty?
92
+
93
+ fields = keys.map do |key|
94
+ # Check if the type itself is optional (not just the key)
95
+ optional = optional_type?(key.type)
96
+ optional_mark = optional ? "?" : ""
97
+ ts_type = resolve_type(key.type)
98
+ ts_type = "#{ts_type} | null" if optional
99
+ " #{to_camel_case(key.name)}#{optional_mark}: #{ts_type};"
100
+ end
101
+
102
+ <<~TS.strip
103
+ /** #{name} */
104
+ export interface #{name} {
105
+ #{fields.join("\n")}
106
+ }
107
+ TS
108
+ end
109
+
95
110
  def generate_mutation_args
96
111
  return nil if @schema.mutations.empty?
97
112
 
98
113
  interfaces = @schema.mutations.map do |name, handler_class|
99
114
  interface_name = to_interface_name(name)
100
- generate_interface(interface_name, handler_class.arguments)
115
+ generate_mutation_interface(interface_name, handler_class.arguments)
101
116
  end
102
117
 
103
118
  "\n" + interfaces.join("\n\n")
104
119
  end
105
120
 
106
- def generate_interface(name, arguments)
107
- if arguments.empty?
108
- return <<~TS.strip
109
- /** #{name} */
110
- export interface #{name} {}
111
- TS
112
- end
113
-
114
- fields = arguments.map do |arg_name, arg|
115
- optional = arg.optional? ? "?" : ""
116
- ts_type = resolve_type(arg.type)
117
- description = arg.description ? " /** #{arg.description} */\n" : ""
118
- "#{description} #{to_camel_case(arg_name)}#{optional}: #{ts_type};"
121
+ def generate_mutation_interface(name, arguments)
122
+ return empty_interface(name) if arguments.empty?
123
+
124
+ fields = arguments.map do |arg_name, config|
125
+ type = config[:type]
126
+ optional = optional_type?(type)
127
+ optional_mark = optional ? "?" : ""
128
+ ts_type = resolve_type(type)
129
+ ts_type = "#{ts_type} | null" if optional
130
+ description = config[:description] ? " /** #{config[:description]} */\n" : ""
131
+ "#{description} #{to_camel_case(arg_name)}#{optional_mark}: #{ts_type};"
119
132
  end
120
133
 
121
134
  <<~TS.strip
@@ -126,6 +139,13 @@ module ZeroRuby
126
139
  TS
127
140
  end
128
141
 
142
+ def empty_interface(name)
143
+ <<~TS.strip
144
+ /** #{name} */
145
+ export interface #{name} {}
146
+ TS
147
+ end
148
+
129
149
  def generate_mutation_map
130
150
  return nil if @schema.mutations.empty?
131
151
 
@@ -140,27 +160,75 @@ module ZeroRuby
140
160
  export interface MutationArgs {
141
161
  #{entries.join("\n")}
142
162
  }
143
-
144
- export type MutationName = keyof MutationArgs;
145
- export type ArgsFor<T extends MutationName> = MutationArgs[T];
146
163
  TS
147
164
  end
148
165
 
149
166
  def resolve_type(type)
150
- type_key = type.to_s
151
-
152
- if TYPE_MAP.key?(type_key)
153
- TYPE_MAP[type_key]
154
- elsif input_object_type?(type)
155
- extract_type_name(type)
156
- else
157
- # Unknown type, default to unknown
158
- "unknown"
167
+ # Handle InputObject classes
168
+ if input_object_type?(type)
169
+ return extract_type_name(type)
170
+ end
171
+
172
+ # Unwrap the dry-type to get the primitive
173
+ primitive = unwrap_primitive(type)
174
+
175
+ # Check if it's a nested InputObject after unwrapping
176
+ if input_object_type?(primitive)
177
+ return extract_type_name(primitive)
159
178
  end
179
+
180
+ # Map primitive to TypeScript
181
+ PRIMITIVE_MAP[primitive] || raise(
182
+ ArgumentError,
183
+ "Cannot map type #{type.inspect} (primitive: #{primitive.inspect}) to TypeScript. " \
184
+ "Supported primitives: #{PRIMITIVE_MAP.keys.map(&:name).join(', ')}"
185
+ )
186
+ end
187
+
188
+ # Unwrap dry-types wrappers to get the underlying primitive
189
+ def unwrap_primitive(type)
190
+ # Check for Sum type (optional = NilClass | type)
191
+ # For optional types, left is NilClass, right is the actual type
192
+ if type.respond_to?(:left) && type.respond_to?(:right)
193
+ left_primitive = unwrap_primitive(type.left)
194
+ right_primitive = unwrap_primitive(type.right)
195
+ # Return the non-NilClass side
196
+ return right_primitive if left_primitive == NilClass
197
+ return left_primitive if right_primitive == NilClass
198
+ # If neither is NilClass, return left (shouldn't happen for optional)
199
+ return left_primitive
200
+ end
201
+
202
+ # Try to get the primitive directly
203
+ if type.respond_to?(:primitive)
204
+ return type.primitive
205
+ end
206
+
207
+ # Check for Constrained type
208
+ if type.respond_to?(:type) && type.class.name&.include?("Constrained")
209
+ return unwrap_primitive(type.type)
210
+ end
211
+
212
+ # Check for Default type
213
+ if type.respond_to?(:type) && type.class.name&.include?("Default")
214
+ return unwrap_primitive(type.type)
215
+ end
216
+
217
+ # Check for other wrappers with .type method
218
+ if type.respond_to?(:type)
219
+ return unwrap_primitive(type.type)
220
+ end
221
+
222
+ type
223
+ end
224
+
225
+ def optional_type?(type)
226
+ return false unless type.respond_to?(:optional?)
227
+ type.optional? || (type.respond_to?(:default?) && type.default?)
160
228
  end
161
229
 
162
230
  def input_object_type?(type)
163
- defined?(ZeroRuby::InputObject) && type.is_a?(Class) && type < ZeroRuby::InputObject
231
+ type.is_a?(Class) && type < ZeroRuby::InputObject
164
232
  end
165
233
 
166
234
  def extract_type_name(type)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ZeroRuby
4
- VERSION = "0.1.0.alpha2"
4
+ VERSION = "0.1.0.alpha4"
5
5
  end
data/lib/zero_ruby.rb CHANGED
@@ -4,33 +4,13 @@ require_relative "zero_ruby/version"
4
4
  require_relative "zero_ruby/configuration"
5
5
  require_relative "zero_ruby/errors"
6
6
 
7
- # Types (must be loaded before InputObject/Mutation which use them)
8
- require_relative "zero_ruby/types/base_type"
9
- require_relative "zero_ruby/types/string"
10
- require_relative "zero_ruby/types/integer"
11
- require_relative "zero_ruby/types/float"
12
- require_relative "zero_ruby/types/boolean"
13
- require_relative "zero_ruby/types/id"
14
- require_relative "zero_ruby/types/big_int"
15
- require_relative "zero_ruby/types/iso8601_date"
16
- require_relative "zero_ruby/types/iso8601_date_time"
7
+ # Types module (dry-types based)
8
+ require_relative "zero_ruby/types"
17
9
 
18
10
  # Type name shortcuts (ID, Boolean, etc. without ZeroRuby::Types:: prefix)
19
- # Must be loaded before InputObject/Mutation which include this module
20
11
  require_relative "zero_ruby/type_names"
21
12
 
22
- # Validators
23
- require_relative "zero_ruby/validators/length_validator"
24
- require_relative "zero_ruby/validators/numericality_validator"
25
- require_relative "zero_ruby/validators/format_validator"
26
- require_relative "zero_ruby/validators/inclusion_validator"
27
- require_relative "zero_ruby/validators/exclusion_validator"
28
- require_relative "zero_ruby/validators/allow_blank_validator"
29
- require_relative "zero_ruby/validators/allow_null_validator"
30
-
31
13
  # Core classes
32
- require_relative "zero_ruby/argument"
33
- require_relative "zero_ruby/validator"
34
14
  require_relative "zero_ruby/input_object"
35
15
  require_relative "zero_ruby/mutation"
36
16
  require_relative "zero_ruby/schema"
@@ -39,6 +19,7 @@ require_relative "zero_ruby/typescript_generator"
39
19
  # LMID (Last Mutation ID) Tracking
40
20
  require_relative "zero_ruby/lmid_store"
41
21
  require_relative "zero_ruby/lmid_stores/active_record_store"
22
+ require_relative "zero_ruby/zero_client"
42
23
  require_relative "zero_ruby/push_processor"
43
24
 
44
25
  # ZeroRuby - A Ruby gem for handling Zero mutations with type safety
@@ -46,19 +27,19 @@ require_relative "zero_ruby/push_processor"
46
27
  # @example Basic usage with InputObject
47
28
  # # Define an input type
48
29
  # class Types::PostInput < ZeroRuby::InputObject
49
- # argument :id, ID, required: true
50
- # argument :title, String, required: true
51
- # argument :body, String, required: false
30
+ # argument :id, ZeroRuby::Types::ID
31
+ # argument :title, ZeroRuby::Types::String.constrained(min_size: 1, max_size: 200)
32
+ # argument :body, ZeroRuby::Types::String.optional
52
33
  # end
53
34
  #
54
35
  # # Define a mutation
55
36
  # class Mutations::PostCreate < ZeroRuby::Mutation
56
- # argument :post_input, Types::PostInput, required: true
57
- # argument :notify, Boolean, required: false
37
+ # argument :post_input, Types::PostInput
38
+ # argument :notify, ZeroRuby::Types::Boolean.default(false)
58
39
  #
59
- # def execute(post_input:, notify: false)
60
- # Post.create!(**post_input)
61
- # notify_subscribers if notify
40
+ # def execute
41
+ # Post.create!(**args[:post_input])
42
+ # notify_subscribers if args[:notify]
62
43
  # end
63
44
  # end
64
45
  #
@@ -80,8 +61,4 @@ require_relative "zero_ruby/push_processor"
80
61
  # end
81
62
  # end
82
63
  module ZeroRuby
83
- # Convenience method for quick type access
84
- module Types
85
- # Already defined in individual type files
86
- end
87
64
  end
metadata CHANGED
@@ -1,14 +1,56 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zero_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.alpha2
4
+ version: 0.1.0.alpha4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Serban
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-12-21 00:00:00.000000000 Z
10
+ date: 2025-12-22 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: dry-struct
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.6'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.6'
26
+ - !ruby/object:Gem::Dependency
27
+ name: dry-types
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.7'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.7'
40
+ - !ruby/object:Gem::Dependency
41
+ name: dry-validation
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.10'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.10'
12
54
  - !ruby/object:Gem::Dependency
13
55
  name: bundler
14
56
  requirement: !ruby/object:Gem::Requirement
@@ -88,8 +130,8 @@ files:
88
130
  - LICENSE.txt
89
131
  - README.md
90
132
  - lib/zero_ruby.rb
91
- - lib/zero_ruby/argument.rb
92
133
  - lib/zero_ruby/configuration.rb
134
+ - lib/zero_ruby/error_formatter.rb
93
135
  - lib/zero_ruby/errors.rb
94
136
  - lib/zero_ruby/input_object.rb
95
137
  - lib/zero_ruby/lmid_store.rb
@@ -98,24 +140,8 @@ files:
98
140
  - lib/zero_ruby/push_processor.rb
99
141
  - lib/zero_ruby/schema.rb
100
142
  - lib/zero_ruby/type_names.rb
101
- - lib/zero_ruby/types/base_type.rb
102
- - lib/zero_ruby/types/big_int.rb
103
- - lib/zero_ruby/types/boolean.rb
104
- - lib/zero_ruby/types/float.rb
105
- - lib/zero_ruby/types/id.rb
106
- - lib/zero_ruby/types/integer.rb
107
- - lib/zero_ruby/types/iso8601_date.rb
108
- - lib/zero_ruby/types/iso8601_date_time.rb
109
- - lib/zero_ruby/types/string.rb
143
+ - lib/zero_ruby/types.rb
110
144
  - lib/zero_ruby/typescript_generator.rb
111
- - lib/zero_ruby/validator.rb
112
- - lib/zero_ruby/validators/allow_blank_validator.rb
113
- - lib/zero_ruby/validators/allow_null_validator.rb
114
- - lib/zero_ruby/validators/exclusion_validator.rb
115
- - lib/zero_ruby/validators/format_validator.rb
116
- - lib/zero_ruby/validators/inclusion_validator.rb
117
- - lib/zero_ruby/validators/length_validator.rb
118
- - lib/zero_ruby/validators/numericality_validator.rb
119
145
  - lib/zero_ruby/version.rb
120
146
  - lib/zero_ruby/zero_client.rb
121
147
  homepage: https://github.com/alse/zero-ruby
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ZeroRuby
4
- # Represents a declared argument for a mutation.
5
- # Holds type, required status, and validation configuration.
6
- class Argument
7
- # Sentinel value to distinguish "no default provided" from "default is nil"
8
- NOT_PROVIDED = Object.new.freeze
9
-
10
- # Maps Ruby built-in classes to ZeroRuby types.
11
- # This allows using String, Integer, Float directly in argument declarations.
12
- RUBY_TYPE_MAP = {
13
- ::String => -> { ZeroRuby::Types::String },
14
- ::Integer => -> { ZeroRuby::Types::Integer },
15
- ::Float => -> { ZeroRuby::Types::Float }
16
- }.freeze
17
-
18
- attr_reader :name, :type, :required, :validators, :default, :description
19
-
20
- def initialize(name:, type:, required: true, validates: nil, default: NOT_PROVIDED, description: nil, **options)
21
- @name = name.to_sym
22
- @type = resolve_type(type)
23
- @required = required
24
- @validators = validates || {}
25
- @has_default = default != NOT_PROVIDED
26
- @default = @has_default ? default : nil
27
- @description = description
28
- @options = options
29
- end
30
-
31
- def required?
32
- @required
33
- end
34
-
35
- def optional?
36
- !@required
37
- end
38
-
39
- def has_default?
40
- @has_default
41
- end
42
-
43
- # Coerce and validate a raw input value
44
- # @param raw_value [Object] The raw input value
45
- # @param ctx [Hash] The context hash
46
- # @return [Object] The coerced value
47
- def coerce(raw_value, ctx = nil)
48
- value = (raw_value.nil? && has_default?) ? @default : raw_value
49
-
50
- # Handle InputObject types (they use .coerce instead of .coerce_input)
51
- if input_object_type?
52
- @type.coerce(value, ctx)
53
- else
54
- @type.coerce_input(value, ctx)
55
- end
56
- end
57
-
58
- private
59
-
60
- # Resolve a type reference to a ZeroRuby type.
61
- # Handles Ruby built-in classes (String, Integer, Float) by mapping them
62
- # to the corresponding ZeroRuby::Types class.
63
- def resolve_type(type)
64
- if RUBY_TYPE_MAP.key?(type)
65
- RUBY_TYPE_MAP[type].call
66
- else
67
- type
68
- end
69
- end
70
-
71
- def input_object_type?
72
- defined?(ZeroRuby::InputObject) && @type.is_a?(Class) && @type < ZeroRuby::InputObject
73
- end
74
- end
75
- end
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ZeroRuby
4
- module Types
5
- # Base class for all types. Provides the interface for type coercion.
6
- class BaseType
7
- class << self
8
- def name
9
- raise NotImplementedError, "Subclasses must implement .name"
10
- end
11
-
12
- def coerce_input(value, _ctx = nil)
13
- raise NotImplementedError, "Subclasses must implement .coerce_input"
14
- end
15
-
16
- def valid?(value)
17
- coerce_input(value)
18
- true
19
- rescue CoercionError
20
- false
21
- end
22
-
23
- protected
24
-
25
- # Helper to raise CoercionError with consistent formatting
26
- # @param value [Object] The invalid value
27
- # @param message [String, nil] Custom message (optional)
28
- def coercion_error!(value, message = nil)
29
- displayed_value = format_value_for_error(value)
30
- msg = message || "#{displayed_value} is not a valid #{name}"
31
- raise CoercionError.new(msg, value: value, expected_type: name)
32
- end
33
-
34
- private
35
-
36
- # Format a value for display in error messages
37
- # Truncates long strings and handles various types
38
- def format_value_for_error(value)
39
- case value
40
- when ::String
41
- truncated = (value.length > 50) ? "#{value[0, 50]}..." : value
42
- "'#{truncated}'"
43
- when ::Symbol
44
- ":#{value}"
45
- when ::NilClass
46
- "nil"
47
- else
48
- value.inspect.then { |s| (s.length > 50) ? "#{s[0, 50]}..." : s }
49
- end
50
- end
51
- end
52
- end
53
- end
54
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base_type"
4
-
5
- module ZeroRuby
6
- module Types
7
- # BigInt type for large integers beyond 32-bit range.
8
- # Ruby's Integer handles arbitrary precision natively.
9
- # Accepts integers and numeric strings, coerces to Integer.
10
- class BigInt < BaseType
11
- class << self
12
- def name
13
- "BigInt"
14
- end
15
-
16
- def coerce_input(value, _ctx = nil)
17
- return nil if value.nil?
18
- return value if value.is_a?(::Integer)
19
-
20
- if value.is_a?(::String)
21
- coercion_error!(value, "empty string is not a valid #{name}") if value.empty?
22
- result = Kernel.Integer(value, exception: false)
23
- coercion_error!(value) if result.nil?
24
- result
25
- else
26
- coercion_error!(value, "#{format_value_for_error(value)} (#{value.class}) cannot be coerced to #{name}")
27
- end
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base_type"
4
-
5
- module ZeroRuby
6
- module Types
7
- class Boolean < BaseType
8
- TRUTHY_VALUES = [true, "true", "1", 1].freeze
9
- FALSY_VALUES = [false, "false", "0", 0].freeze
10
-
11
- class << self
12
- def name
13
- "Boolean"
14
- end
15
-
16
- def coerce_input(value, _ctx = nil)
17
- return nil if value.nil?
18
-
19
- if TRUTHY_VALUES.include?(value)
20
- true
21
- elsif FALSY_VALUES.include?(value)
22
- false
23
- else
24
- coercion_error!(value, "#{format_value_for_error(value)} is not a valid #{name}; expected true, false, \"true\", \"false\", 0, or 1")
25
- end
26
- end
27
- end
28
- end
29
- end
30
- end