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
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base_type"
4
-
5
- module ZeroRuby
6
- module Types
7
- class Float < BaseType
8
- class << self
9
- def name
10
- "Float"
11
- end
12
-
13
- def coerce_input(value, _ctx = nil)
14
- return nil if value.nil?
15
- return value if value.is_a?(::Float)
16
-
17
- if value.is_a?(::Integer)
18
- value.to_f
19
- elsif value.is_a?(::String)
20
- coercion_error!(value, "empty string is not a valid #{name}") if value.empty?
21
- result = Kernel.Float(value, exception: false)
22
- coercion_error!(value) if result.nil?
23
- result
24
- else
25
- coercion_error!(value, "#{format_value_for_error(value)} (#{value.class}) cannot be coerced to #{name}")
26
- end
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base_type"
4
-
5
- module ZeroRuby
6
- module Types
7
- # ID type for unique identifiers (database PKs, FKs, etc.)
8
- # Accepts strings and integers, always coerces to String.
9
- class ID < BaseType
10
- class << self
11
- def name
12
- "ID"
13
- end
14
-
15
- def coerce_input(value, _ctx = nil)
16
- return nil if value.nil?
17
-
18
- case value
19
- when ::String
20
- coercion_error!(value, "empty string is not a valid #{name}") if value.empty?
21
- value
22
- when ::Integer
23
- value.to_s
24
- when ::Symbol
25
- value.to_s
26
- else
27
- coercion_error!(value, "#{format_value_for_error(value)} (#{value.class}) cannot be coerced to #{name}")
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base_type"
4
-
5
- module ZeroRuby
6
- module Types
7
- class Integer < BaseType
8
- class << self
9
- def name
10
- "Integer"
11
- end
12
-
13
- def coerce_input(value, _ctx = nil)
14
- return nil if value.nil?
15
- return value if value.is_a?(::Integer)
16
-
17
- if value.is_a?(::String)
18
- coercion_error!(value, "empty string is not a valid #{name}") if value.empty?
19
- result = Kernel.Integer(value, exception: false)
20
- coercion_error!(value) if result.nil?
21
- result
22
- elsif value.is_a?(::Float)
23
- value.to_i
24
- else
25
- coercion_error!(value, "#{format_value_for_error(value)} (#{value.class}) cannot be coerced to #{name}")
26
- end
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "date"
4
- require_relative "base_type"
5
-
6
- module ZeroRuby
7
- module Types
8
- # ISO8601Date type for date values.
9
- # Accepts ISO8601 formatted date strings, Date, Time, and DateTime objects.
10
- # Coerces to Date.
11
- class ISO8601Date < BaseType
12
- class << self
13
- def name
14
- "ISO8601Date"
15
- end
16
-
17
- def coerce_input(value, _ctx = nil)
18
- return nil if value.nil?
19
-
20
- case value
21
- when ::Date
22
- value
23
- when ::Time, ::DateTime
24
- value.to_date
25
- when ::String
26
- coercion_error!(value, "empty string is not a valid #{name}") if value.empty?
27
- parse_iso8601_date(value)
28
- else
29
- coercion_error!(value, "#{format_value_for_error(value)} (#{value.class}) cannot be coerced to #{name}")
30
- end
31
- end
32
-
33
- private
34
-
35
- def parse_iso8601_date(value)
36
- Date.iso8601(value)
37
- rescue ArgumentError
38
- coercion_error!(value, "#{format_value_for_error(value)} is not a valid ISO8601 date")
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "time"
4
- require_relative "base_type"
5
-
6
- module ZeroRuby
7
- module Types
8
- # ISO8601DateTime type for datetime values.
9
- # Accepts ISO8601 formatted strings, Time, and DateTime objects.
10
- # Coerces to Time.
11
- class ISO8601DateTime < BaseType
12
- class << self
13
- def name
14
- "ISO8601DateTime"
15
- end
16
-
17
- def coerce_input(value, _ctx = nil)
18
- return nil if value.nil?
19
-
20
- case value
21
- when ::Time
22
- value
23
- when ::DateTime
24
- value.to_time
25
- when ::String
26
- coercion_error!(value, "empty string is not a valid #{name}") if value.empty?
27
- parse_iso8601(value)
28
- else
29
- coercion_error!(value, "#{format_value_for_error(value)} (#{value.class}) cannot be coerced to #{name}")
30
- end
31
- end
32
-
33
- private
34
-
35
- def parse_iso8601(value)
36
- Time.iso8601(value)
37
- rescue ArgumentError
38
- coercion_error!(value, "#{format_value_for_error(value)} is not a valid ISO8601 datetime")
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base_type"
4
-
5
- module ZeroRuby
6
- module Types
7
- class String < BaseType
8
- class << self
9
- def name
10
- "String"
11
- end
12
-
13
- def coerce_input(value, _ctx = nil)
14
- return nil if value.nil?
15
- value.to_s
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ZeroRuby
4
- # Base class for argument validators.
5
- # Inspired by graphql-ruby's validation system.
6
- class Validator
7
- class << self
8
- # Registry of validator classes by name
9
- def validators
10
- @validators ||= {}
11
- end
12
-
13
- # Register a validator class
14
- def register(name, klass)
15
- validators[name.to_sym] = klass
16
- end
17
-
18
- # Get a validator class by name
19
- def get(name)
20
- validators[name.to_sym]
21
- end
22
-
23
- # Run all validations on a value
24
- # @param validators_config [Hash] Configuration hash for validators
25
- # @param mutation [ZeroRuby::Mutation] The mutation instance
26
- # @param ctx [Hash] The context hash
27
- # @param value [Object] The value to validate
28
- # @return [Array<String>] Array of error messages
29
- def validate!(validators_config, mutation, ctx, value)
30
- return [] if validators_config.nil? || validators_config.empty?
31
-
32
- errors = []
33
-
34
- validators_config.each do |validator_name, config|
35
- validator_class = get(validator_name)
36
- next unless validator_class
37
-
38
- validator = validator_class.new(config)
39
- result = validator.validate(mutation, ctx, value)
40
- errors.concat(Array(result)) if result
41
- end
42
-
43
- errors
44
- end
45
- end
46
-
47
- attr_reader :config
48
-
49
- def initialize(config)
50
- @config = config
51
- end
52
-
53
- # Validate a value
54
- # @param mutation [ZeroRuby::Mutation] The mutation instance
55
- # @param ctx [Hash] The context hash
56
- # @param value [Object] The value to validate
57
- # @return [String, Array<String>, nil] Error message(s) or nil if valid
58
- def validate(mutation, ctx, value)
59
- raise NotImplementedError, "Subclasses must implement #validate"
60
- end
61
-
62
- protected
63
-
64
- # Helper to format error messages
65
- def error_message(message)
66
- message
67
- end
68
- end
69
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../validator"
4
-
5
- module ZeroRuby
6
- module Validators
7
- # Validates that a value is not blank when allow_blank is false.
8
- #
9
- # @example
10
- # validates: { allow_blank: false }
11
- class AllowBlankValidator < Validator
12
- def validate(mutation, ctx, value)
13
- # If allow_blank is true (or truthy), blank values are allowed
14
- return nil if config == true || config
15
-
16
- # Check if value is blank (nil, empty string, or whitespace-only string)
17
- is_blank = value.nil? ||
18
- (value.respond_to?(:empty?) && value.empty?) ||
19
- (value.is_a?(::String) && value.strip.empty?)
20
-
21
- if is_blank
22
- return "can't be blank"
23
- end
24
-
25
- nil
26
- end
27
- end
28
-
29
- Validator.register(:allow_blank, AllowBlankValidator)
30
- end
31
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../validator"
4
-
5
- module ZeroRuby
6
- module Validators
7
- # Validates that a value is not null when allow_null is false.
8
- #
9
- # @example
10
- # validates: { allow_null: false }
11
- class AllowNullValidator < Validator
12
- def validate(mutation, ctx, value)
13
- # If allow_null is true (or truthy), null values are allowed
14
- return nil if config == true || config
15
-
16
- if value.nil?
17
- return "can't be null"
18
- end
19
-
20
- nil
21
- end
22
- end
23
-
24
- Validator.register(:allow_null, AllowNullValidator)
25
- end
26
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../validator"
4
-
5
- module ZeroRuby
6
- module Validators
7
- # Validates that a value is NOT included in a given set.
8
- #
9
- # @example
10
- # validates: { exclusion: { in: ["admin", "root", "system"] } }
11
- class ExclusionValidator < Validator
12
- def validate(mutation, ctx, value)
13
- return nil if value.nil?
14
-
15
- excluded = config[:in] || config[:within]
16
- return nil unless excluded
17
-
18
- if excluded.respond_to?(:include?) ? excluded.include?(value) : excluded.cover?(value)
19
- message = config[:message] || "is reserved"
20
- return message
21
- end
22
-
23
- nil
24
- end
25
- end
26
-
27
- Validator.register(:exclusion, ExclusionValidator)
28
- end
29
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../validator"
4
-
5
- module ZeroRuby
6
- module Validators
7
- # Validates that a value matches a regular expression.
8
- #
9
- # @example
10
- # validates: { format: { with: /\A[a-z0-9_]+\z/ } }
11
- # validates: { format: { without: /[<>]/ } }
12
- class FormatValidator < Validator
13
- def validate(mutation, ctx, value)
14
- return nil if value.nil?
15
-
16
- str_value = value.to_s
17
- errors = []
18
-
19
- if config[:with] && !str_value.match?(config[:with])
20
- message = config[:message] || "is invalid"
21
- errors << message
22
- end
23
-
24
- if config[:without] && str_value.match?(config[:without])
25
- message = config[:message] || "is invalid"
26
- errors << message
27
- end
28
-
29
- errors.empty? ? nil : errors
30
- end
31
- end
32
-
33
- Validator.register(:format, FormatValidator)
34
- end
35
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../validator"
4
-
5
- module ZeroRuby
6
- module Validators
7
- # Validates that a value is included in a given set.
8
- #
9
- # @example
10
- # validates: { inclusion: { in: ["draft", "published", "archived"] } }
11
- # validates: { inclusion: { in: 1..10 } }
12
- class InclusionValidator < Validator
13
- def validate(mutation, ctx, value)
14
- return nil if value.nil?
15
-
16
- allowed = config[:in] || config[:within]
17
- return nil unless allowed
18
-
19
- unless allowed.respond_to?(:include?) ? allowed.include?(value) : allowed.cover?(value)
20
- message = config[:message] || "is not included in the list"
21
- return message
22
- end
23
-
24
- nil
25
- end
26
- end
27
-
28
- Validator.register(:inclusion, InclusionValidator)
29
- end
30
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../validator"
4
-
5
- module ZeroRuby
6
- module Validators
7
- # Validates the length of a string or array value.
8
- #
9
- # @example
10
- # validates: { length: { minimum: 1, maximum: 200 } }
11
- # validates: { length: { is: 10 } }
12
- # validates: { length: { in: 5..10 } }
13
- class LengthValidator < Validator
14
- def validate(mutation, ctx, value)
15
- return nil if value.nil?
16
-
17
- length = value.respond_to?(:length) ? value.length : value.to_s.length
18
- errors = []
19
-
20
- if config[:minimum] && length < config[:minimum]
21
- errors << "is too short (minimum is #{config[:minimum]})"
22
- end
23
-
24
- if config[:maximum] && length > config[:maximum]
25
- errors << "is too long (maximum is #{config[:maximum]})"
26
- end
27
-
28
- if config[:is] && length != config[:is]
29
- errors << "is the wrong length (should be #{config[:is]})"
30
- end
31
-
32
- if config[:in] && !config[:in].cover?(length)
33
- errors << "length is not in #{config[:in]}"
34
- end
35
-
36
- errors.empty? ? nil : errors
37
- end
38
- end
39
-
40
- Validator.register(:length, LengthValidator)
41
- end
42
- end
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../validator"
4
-
5
- module ZeroRuby
6
- module Validators
7
- # Validates numeric constraints on a value.
8
- #
9
- # @example
10
- # validates: { numericality: { greater_than: 0 } }
11
- # validates: { numericality: { less_than_or_equal_to: 100 } }
12
- # validates: { numericality: { equal_to: 42 } }
13
- # validates: { numericality: { odd: true } }
14
- # validates: { numericality: { even: true } }
15
- class NumericalityValidator < Validator
16
- def validate(mutation, ctx, value)
17
- return nil if value.nil?
18
-
19
- unless value.is_a?(Numeric)
20
- return "is not a number"
21
- end
22
-
23
- errors = []
24
-
25
- if config[:greater_than] && value <= config[:greater_than]
26
- errors << "must be greater than #{config[:greater_than]}"
27
- end
28
-
29
- if config[:greater_than_or_equal_to] && value < config[:greater_than_or_equal_to]
30
- errors << "must be greater than or equal to #{config[:greater_than_or_equal_to]}"
31
- end
32
-
33
- if config[:less_than] && value >= config[:less_than]
34
- errors << "must be less than #{config[:less_than]}"
35
- end
36
-
37
- if config[:less_than_or_equal_to] && value > config[:less_than_or_equal_to]
38
- errors << "must be less than or equal to #{config[:less_than_or_equal_to]}"
39
- end
40
-
41
- if config[:equal_to] && value != config[:equal_to]
42
- errors << "must be equal to #{config[:equal_to]}"
43
- end
44
-
45
- if config[:other_than] && value == config[:other_than]
46
- errors << "must be other than #{config[:other_than]}"
47
- end
48
-
49
- if config[:odd] && value.to_i.even?
50
- errors << "must be odd"
51
- end
52
-
53
- if config[:even] && value.to_i.odd?
54
- errors << "must be even"
55
- end
56
-
57
- errors.empty? ? nil : errors
58
- end
59
- end
60
-
61
- Validator.register(:numericality, NumericalityValidator)
62
- end
63
- end