Class: ZeroRuby::Mutation
- Inherits:
-
Object
- Object
- ZeroRuby::Mutation
- Includes:
- TypeNames
- Defined in:
- lib/zero_ruby/mutation.rb
Overview
Base class for Zero mutations. Provides argument DSL with dry-types validation.
Includes ZeroRuby::TypeNames for convenient type access via the Types module (e.g., Types::String, Types::ID, Types::Boolean).
By default (auto_transact: true), the entire execute method runs inside a transaction with LMID tracking. For 3-phase control, set auto_transact false.
Constant Summary
Constants included from TypeNames
Instance Attribute Summary collapse
-
#args ⇒ Object
readonly
The validated arguments hash.
-
#ctx ⇒ Object
readonly
The context hash containing current_user, etc.
Class Method Summary collapse
-
.argument(name, type, description: nil) ⇒ Object
Declare an argument for this mutation.
-
.arguments ⇒ Hash<Symbol, Hash>
Get all declared arguments for this mutation (including inherited).
-
.coerce_and_validate!(raw_args) ⇒ Hash
Coerce and validate raw arguments.
-
.skip_auto_transaction ⇒ void
Opt-out of auto-transaction wrapping.
-
.skip_auto_transaction? ⇒ Boolean
Check if auto-transaction is skipped for this mutation.
Instance Method Summary collapse
-
#call(&transact_proc) ⇒ Hash
Execute the mutation.
-
#initialize(raw_args, ctx) ⇒ Mutation
constructor
Initialize a mutation with raw arguments and context.
Constructor Details
#initialize(raw_args, ctx) ⇒ Mutation
Initialize a mutation with raw arguments and context
206 207 208 209 |
# File 'lib/zero_ruby/mutation.rb', line 206 def initialize(raw_args, ctx) @ctx = ctx @args = self.class.coerce_and_validate!(raw_args) end |
Instance Attribute Details
#args ⇒ Object (readonly)
The validated arguments hash
54 55 56 |
# File 'lib/zero_ruby/mutation.rb', line 54 def args @args end |
#ctx ⇒ Object (readonly)
The context hash containing current_user, etc.
51 52 53 |
# File 'lib/zero_ruby/mutation.rb', line 51 def ctx @ctx end |
Class Method Details
.argument(name, type, description: nil) ⇒ Object
Declare an argument for this mutation
76 77 78 79 80 81 82 |
# File 'lib/zero_ruby/mutation.rb', line 76 def argument(name, type, description: nil) arguments[name.to_sym] = { type: type, description: description, name: name.to_sym } end |
.arguments ⇒ Hash<Symbol, Hash>
Get all declared arguments for this mutation (including inherited)
86 87 88 89 90 91 92 |
# File 'lib/zero_ruby/mutation.rb', line 86 def arguments @arguments ||= if superclass.respond_to?(:arguments) superclass.arguments.dup else {} end end |
.coerce_and_validate!(raw_args) ⇒ Hash
Coerce and validate raw arguments. Collects ALL validation errors (missing fields, type coercion, constraints) and raises a single ValidationError with all issues.
Uses type.try(value) which returns a Result instead of raising, allowing us to collect all errors in one pass rather than failing on the first one. Works for both Dry::Types (scalars) and Dry::Struct (InputObjects).
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/zero_ruby/mutation.rb', line 105 def coerce_and_validate!(raw_args) # Result hash: symbol keys → coerced values (strings, integers, InputObject instances, etc.) # eg: # raw_args: {"name" => "test", "count" => "5"} # string keys, raw values # validated: {name: "test", count: 5} # symbol keys, coerced values validated = {} errors = [] arguments.each do |name, config| type = config[:type] str_key = name.to_s key_present = raw_args.key?(str_key) value = raw_args[str_key] is_input_object = input_object_type?(type) # Missing key: use default if available, otherwise error if required unless key_present if has_default?(type) validated[name] = get_default(type) elsif required_type?(type) errors << "#{name} is required" end # Optional fields without defaults are simply omitted from result next end # Explicit null: InputObjects always allow nil (they handle optionality internally), # scalars only allow nil if the type is optional if value.nil? if is_input_object || !required_type?(type) validated[name] = nil else errors << "#{name} is required" end next end # Coerce value: type.try returns Result instead of raising, so we can # collect all errors. Works for both Dry::Types and Dry::Struct. result = type.try(value) if result.failure? errors << format_type_error(name, result.error, is_input_object) else validated[name] = result.input end end raise ValidationError.new(errors) if errors.any? validated end |
.skip_auto_transaction ⇒ void
This method returns an undefined value.
Opt-out of auto-transaction wrapping. By default, execute is wrapped in a transaction with LMID tracking. Call this to use explicit 3-phase model where you must call transact { }.
62 63 64 |
# File 'lib/zero_ruby/mutation.rb', line 62 def skip_auto_transaction @skip_auto_transaction = true end |
.skip_auto_transaction? ⇒ Boolean
Check if auto-transaction is skipped for this mutation
68 69 70 |
# File 'lib/zero_ruby/mutation.rb', line 68 def skip_auto_transaction? @skip_auto_transaction == true end |
Instance Method Details
#call(&transact_proc) ⇒ Hash
Execute the mutation
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/zero_ruby/mutation.rb', line 216 def call(&transact_proc) @transact_proc = transact_proc @transact_called = false if self.class.skip_auto_transaction? # Manual mode: Use defined mutation calls transact {} data = execute(**@args) raise TransactNotCalledError.new unless @transact_called else # Auto mode: wrap entire execute in transaction data = transact_proc.call { execute(**@args) } end result = {} result[:data] = data if data.is_a?(Hash) && !data.empty? result end |