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.
- checksums.yaml +4 -4
- data/README.md +124 -70
- data/lib/zero_ruby/configuration.rb +0 -5
- data/lib/zero_ruby/error_formatter.rb +171 -0
- data/lib/zero_ruby/errors.rb +10 -1
- data/lib/zero_ruby/input_object.rb +56 -93
- data/lib/zero_ruby/lmid_stores/active_record_store.rb +0 -1
- data/lib/zero_ruby/mutation.rb +201 -77
- data/lib/zero_ruby/push_processor.rb +108 -34
- data/lib/zero_ruby/schema.rb +38 -14
- data/lib/zero_ruby/type_names.rb +13 -8
- data/lib/zero_ruby/types.rb +54 -0
- data/lib/zero_ruby/typescript_generator.rb +126 -58
- data/lib/zero_ruby/version.rb +1 -1
- data/lib/zero_ruby.rb +11 -34
- metadata +46 -20
- data/lib/zero_ruby/argument.rb +0 -75
- data/lib/zero_ruby/types/base_type.rb +0 -54
- data/lib/zero_ruby/types/big_int.rb +0 -32
- data/lib/zero_ruby/types/boolean.rb +0 -30
- data/lib/zero_ruby/types/float.rb +0 -31
- data/lib/zero_ruby/types/id.rb +0 -33
- data/lib/zero_ruby/types/integer.rb +0 -31
- data/lib/zero_ruby/types/iso8601_date.rb +0 -43
- data/lib/zero_ruby/types/iso8601_date_time.rb +0 -43
- data/lib/zero_ruby/types/string.rb +0 -20
- data/lib/zero_ruby/validator.rb +0 -69
- data/lib/zero_ruby/validators/allow_blank_validator.rb +0 -31
- data/lib/zero_ruby/validators/allow_null_validator.rb +0 -26
- data/lib/zero_ruby/validators/exclusion_validator.rb +0 -29
- data/lib/zero_ruby/validators/format_validator.rb +0 -35
- data/lib/zero_ruby/validators/inclusion_validator.rb +0 -30
- data/lib/zero_ruby/validators/length_validator.rb +0 -42
- 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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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,
|
|
74
|
-
|
|
75
|
-
|
|
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] =
|
|
78
|
-
# Recursively collect nested InputObjects
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
107
|
-
if arguments.empty?
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
231
|
+
type.is_a?(Class) && type < ZeroRuby::InputObject
|
|
164
232
|
end
|
|
165
233
|
|
|
166
234
|
def extract_type_name(type)
|
data/lib/zero_ruby/version.rb
CHANGED
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 (
|
|
8
|
-
require_relative "zero_ruby/types
|
|
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
|
|
50
|
-
# argument :title, String,
|
|
51
|
-
# argument :body, String
|
|
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
|
|
57
|
-
# argument :notify, Boolean
|
|
37
|
+
# argument :post_input, Types::PostInput
|
|
38
|
+
# argument :notify, ZeroRuby::Types::Boolean.default(false)
|
|
58
39
|
#
|
|
59
|
-
# def execute
|
|
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.
|
|
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-
|
|
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
|
|
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
|
data/lib/zero_ruby/argument.rb
DELETED
|
@@ -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
|