Module: Operandi::Dsl::Validation

Defined in:
lib/operandi/dsl/validation.rb

Overview

Shared validation logic for DSL modules

Class Method Summary collapse

Class Method Details

.has_argument?(name_sym, service_class) ⇒ Boolean

Check if a name is already defined as an argument

Returns:

  • (Boolean)


105
106
107
108
109
110
# File 'lib/operandi/dsl/validation.rb', line 105

def self.has_argument?(name_sym, service_class)
  # Check own_arguments (current class)
  (service_class.respond_to?(:own_arguments) && service_class.own_arguments.key?(name_sym)) ||
    # Check inherited arguments
    (service_class.superclass.respond_to?(:arguments) && service_class.superclass.arguments.key?(name_sym))
end

.has_output?(name_sym, service_class) ⇒ Boolean

Check if a name is already defined as an output

Returns:

  • (Boolean)


113
114
115
116
117
118
# File 'lib/operandi/dsl/validation.rb', line 113

def self.has_output?(name_sym, service_class)
  # Check own_outputs (current class)
  (service_class.respond_to?(:own_outputs) && service_class.own_outputs.key?(name_sym)) ||
    # Check inherited outputs
    (service_class.superclass.respond_to?(:outputs) && service_class.superclass.outputs.key?(name_sym))
end

.has_step?(name_sym, service_class) ⇒ Boolean

Check if a name is already defined as a step

Returns:

  • (Boolean)


121
122
123
124
125
126
127
# File 'lib/operandi/dsl/validation.rb', line 121

def self.has_step?(name_sym, service_class)
  # Check step_operations (current class) for non-removed steps
  (service_class.respond_to?(:step_operations) &&
   service_class.step_operations.any? { |op| op[:name] == name_sym && op[:action] != :remove }) ||
    # Check inherited steps
    (service_class.superclass.respond_to?(:steps) && service_class.superclass.steps.key?(name_sym))
end

.require_type_enabled_for?(field_type, service_class) ⇒ Boolean

Check if require_type is enabled for the given field type and service class

Parameters:

  • field_type (Symbol)

    the type of field (:argument, :output)

  • service_class (Class)

    the service class to check

Returns:

  • (Boolean)

    whether type is required for the field type



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/operandi/dsl/validation.rb', line 150

def self.require_type_enabled_for?(field_type, service_class)
  config_key = field_type == :argument ? :require_arg_type : :require_output_type

  # Check class-level config in the inheritance chain, then fall back to global config
  klass = service_class
  while klass.respond_to?(:class_config)
    class_config = klass.class_config

    # Check specific config first (require_arg_type or require_output_type)
    return class_config[config_key] if class_config&.key?(config_key)

    # Check convenience config (require_type) for backward compatibility
    return class_config[:require_type] if class_config&.key?(:require_type)

    klass = klass.superclass
  end

  Operandi.config.public_send(config_key)
end

.validate_argument_conflicts!(name_sym, service_class) ⇒ Object

Validate argument name doesn't conflict with outputs or steps



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/operandi/dsl/validation.rb', line 54

def self.validate_argument_conflicts!(name_sym, service_class)
  # Check against existing outputs
  if has_output?(name_sym, service_class)
    raise Operandi::ReservedNameError,
          "Cannot use `#{name_sym}` as argument name in #{service_class} - " \
          "it is already defined as an output"
  end

  # Check against existing steps
  if has_step?(name_sym, service_class)
    raise Operandi::ReservedNameError,
          "Cannot use `#{name_sym}` as argument name in #{service_class} - " \
          "it is already defined as a step"
  end
end

.validate_name_conflicts!(name, field_type, service_class) ⇒ Object

Validate that the name doesn't conflict with other defined names

Parameters:

  • name (Symbol)

    the name to validate

  • field_type (Symbol)

    the type of field being defined (:argument, :output, :step)

  • service_class (Class)

    the service class to check for conflicts



40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/operandi/dsl/validation.rb', line 40

def self.validate_name_conflicts!(name, field_type, service_class)
  name_sym = name.to_sym

  case field_type
  when :argument
    validate_argument_conflicts!(name_sym, service_class)
  when :output
    validate_output_conflicts!(name_sym, service_class)
  when :step
    validate_step_conflicts!(name_sym, service_class)
  end
end

.validate_output_conflicts!(name_sym, service_class) ⇒ Object

Validate output name doesn't conflict with arguments or steps



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/operandi/dsl/validation.rb', line 71

def self.validate_output_conflicts!(name_sym, service_class)
  # Check against existing arguments
  if has_argument?(name_sym, service_class)
    raise Operandi::ReservedNameError,
          "Cannot use `#{name_sym}` as output name in #{service_class} - " \
          "it is already defined as an argument"
  end

  # Check against existing steps
  if has_step?(name_sym, service_class)
    raise Operandi::ReservedNameError,
          "Cannot use `#{name_sym}` as output name in #{service_class} - " \
          "it is already defined as a step"
  end
end

.validate_reserved_name!(name, field_type, service_class) ⇒ Object

Validate that the name is not a reserved word

Parameters:

  • name (Symbol)

    the name to validate

  • field_type (Symbol)

    the type of field (:argument, :output, :step)

  • service_class (Class)

    the service class for error messages

Raises:



27
28
29
30
31
32
33
# File 'lib/operandi/dsl/validation.rb', line 27

def self.validate_reserved_name!(name, field_type, service_class)
  return unless ReservedNames::ALL.include?(name.to_sym)

  raise Operandi::ReservedNameError,
        "Cannot use `#{name}` as #{field_type} name in #{service_class} - " \
        "it is a reserved word that conflicts with gem methods"
end

.validate_step_conflicts!(name_sym, service_class) ⇒ Object

Validate step name doesn't conflict with arguments or outputs



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/operandi/dsl/validation.rb', line 88

def self.validate_step_conflicts!(name_sym, service_class)
  # Check against existing arguments
  if has_argument?(name_sym, service_class)
    raise Operandi::ReservedNameError,
          "Cannot use `#{name_sym}` as step name in #{service_class} - " \
          "it is already defined as an argument"
  end

  # Check against existing outputs
  if has_output?(name_sym, service_class)
    raise Operandi::ReservedNameError,
          "Cannot use `#{name_sym}` as step name in #{service_class} - " \
          "it is already defined as an output"
  end
end

.validate_symbol_name!(name, field_type, service_class) ⇒ Object

Validate that the name is a symbol

Parameters:

  • name (Object)

    the name to validate

  • field_type (Symbol)

    the type of field (:argument, :output, :step)

  • service_class (Class)

    the service class for error messages

Raises:



14
15
16
17
18
19
20
# File 'lib/operandi/dsl/validation.rb', line 14

def self.validate_symbol_name!(name, field_type, service_class)
  return if name.is_a?(Symbol)

  raise Operandi::InvalidNameError,
        "#{field_type.to_s.capitalize} name must be a Symbol, " \
        "got #{name.class} (#{name.inspect}) in #{service_class}"
end

.validate_type_required!(name, field_type, service_class, opts) ⇒ Object

Validate that the type option is provided when require_type is enabled

Parameters:

  • name (Symbol)

    the field name

  • field_type (Symbol)

    the type of field (:argument, :output)

  • service_class (Class)

    the service class for error messages

  • opts (Hash)

    the options hash to check for type

Raises:



135
136
137
138
139
140
141
142
143
# File 'lib/operandi/dsl/validation.rb', line 135

def self.validate_type_required!(name, field_type, service_class, opts)
  return if opts.key?(:type)
  return unless require_type_enabled_for?(field_type, service_class)

  config_name = field_type == :argument ? "require_arg_type" : "require_output_type"
  raise Operandi::MissingTypeError,
        "#{field_type.to_s.capitalize} `#{name}` in #{service_class} must have a type specified " \
        "(#{config_name} is enabled)"
end