Module: ActionSpec::Schema

Defined in:
lib/action_spec/schema.rb,
lib/action_spec/schema/base.rb,
lib/action_spec/schema/field.rb,
lib/action_spec/schema/scalar.rb,
lib/action_spec/schema/array_of.rb,
lib/action_spec/schema/resolver.rb,
lib/action_spec/schema/object_of.rb,
lib/action_spec/schema/type_caster.rb,
lib/action_spec/schema/active_record.rb

Defined Under Namespace

Modules: ActiveRecord Classes: ArrayOf, Base, Field, ObjectOf, Resolver, Scalar, TypeCaster

Constant Summary collapse

Missing =
Object.new.freeze
OPTION_KEYS =
%i[default desc enum range pattern length blank allow_blank example examples].freeze
FIELD_OPTION_KEYS =
(OPTION_KEYS + %i[required transform px px_key validate error error_message]).freeze

Class Method Summary collapse

Class Method Details

.build(type = nil, **options) ⇒ Object



19
20
21
22
23
# File 'lib/action_spec/schema.rb', line 19

def build(type = nil, **options)
  definition = options.symbolize_keys
  definition[:type] = type if type
  from_definition(definition)
end

.build_field(name, definition = nil, required: false, scopes: []) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/action_spec/schema.rb', line 25

def build_field(name, definition = nil, required: false, scopes: [])
  field_required = required_key?(name) || required || explicit_required?(definition)

  Field.new(
    name: field_name(name),
    required: field_required,
    schema: build_field_schema(schema_definition_for_field(definition, required: field_required)),
    transform: explicit_transform(definition),
    validate: explicit_validate(definition),
    error_message: explicit_error_message(definition),
    px_key: explicit_px_key(definition),
    scopes:
  )
end

.build_field_schema(definition) ⇒ Object



84
85
86
87
88
89
90
91
92
# File 'lib/action_spec/schema.rb', line 84

def build_field_schema(definition)
    return from_definition(type: definition) unless definition.is_a?(Hash)

  definition = definition.symbolize_keys
  return from_definition(definition.except(:required)) if definition.key?(:type)
  return from_definition(definition.except(:required)) if (definition.keys - FIELD_OPTION_KEYS).present?

  from_definition(definition.except(:required).merge(type: String))
end

.build_fields(definition_hash, scopes: []) ⇒ Object



69
70
71
72
73
74
# File 'lib/action_spec/schema.rb', line 69

def build_fields(definition_hash, scopes: [])
  definition_hash.each_with_object(ActiveSupport::OrderedHash.new) do |(name, definition), fields|
    field = build_field(name, definition, scopes:)
    fields[field.name] = field
  end
end

.field_name(name) ⇒ Object



76
77
78
# File 'lib/action_spec/schema.rb', line 76

def field_name(name)
  name.to_s.delete_suffix("!").to_sym
end

.from_definition(definition) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/action_spec/schema.rb', line 40

def from_definition(definition)
  return Scalar.new(definition) if definition.is_a?(Class)
  return Scalar.new(definition) if definition.is_a?(Symbol)
  return Scalar.new(String) if definition.blank?
  return ArrayOf.new(from_definition(definition.first)) if definition.is_a?(Array) && definition.one?
  return ArrayOf.new(from_definition(type: nil)) if definition == []
  return Scalar.new(definition) unless definition.is_a?(Hash)

  definition = definition.deep_symbolize_keys
  if definition.key?(:type)
    type = definition[:type]
    options = definition.slice(*OPTION_KEYS)
    return ArrayOf.new(from_definition(type.first), options) if type.is_a?(Array) && type.one?
    return ArrayOf.new(from_definition(type: nil), options) if type == []
    if type.is_a?(Hash)
      return Scalar.new(Object, options) if type.empty?

      return ObjectOf.new(build_fields(type), options)
    end
    if [Object, Hash].include?(type) && definition.except(:type, *OPTION_KEYS).present?
      return ObjectOf.new(build_fields(definition.except(:type, *OPTION_KEYS)), options)
    end

    return Scalar.new(type, options)
  end

  ObjectOf.new(build_fields(definition))
end

.required_key?(name) ⇒ Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/action_spec/schema.rb', line 80

def required_key?(name)
  name.to_s.end_with?("!")
end

.schema_definition?(definition) ⇒ Boolean

Returns:

  • (Boolean)


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/action_spec/schema.rb', line 94

def schema_definition?(definition)
  case definition
  when Array
    definition.one? && schema_definition?(definition.first)
  when Hash
    definition = definition.with_indifferent_access
    return true if definition.key?(:type)
    return true if definition.keys.all? { |key| FIELD_OPTION_KEYS.include?(key.to_sym) }

    definition.any? do |name, value|
      required_key?(name) || schema_definition?(value)
    end
  when Class
    true
  when Symbol
    definition == :boolean || definition == :file || definition == :object
  else
    false
  end
end