Class: Schemable::RelationshipSchemaGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/schemable/relationship_schema_generator.rb

Overview

The RelationshipSchemaGenerator class is responsible for generating the ‘relationships’ part of a JSON:API compliant response. This class generates schemas for each relationship of a model, including ‘belongs_to’ (and has_many) and ‘has_many’ relationships.

See Also:

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model_definition) ⇒ RelationshipSchemaGenerator

Initializes a new RelationshipSchemaGenerator instance.

Examples:

generator = RelationshipSchemaGenerator.new(model_definition)

Parameters:

  • model_definition (ModelDefinition)

    The model definition to generate the schema for.



15
16
17
18
19
# File 'lib/schemable/relationship_schema_generator.rb', line 15

def initialize(model_definition)
  @model_definition = model_definition
  @schema_modifier = SchemaModifier.new
  @relationships = model_definition.relationships
end

Instance Attribute Details

#model_definitionObject (readonly)

Returns the value of attribute model_definition.



7
8
9
# File 'lib/schemable/relationship_schema_generator.rb', line 7

def model_definition
  @model_definition
end

#relationshipsObject (readonly)

Returns the value of attribute relationships.



7
8
9
# File 'lib/schemable/relationship_schema_generator.rb', line 7

def relationships
  @relationships
end

#schema_modifierObject (readonly)

Returns the value of attribute schema_modifier.



7
8
9
# File 'lib/schemable/relationship_schema_generator.rb', line 7

def schema_modifier
  @schema_modifier
end

Instance Method Details

#generate(relationships_to_exclude_from_expansion: [], expand: false) ⇒ Hash

Generates the ‘relationships’ part of the JSON:API response. It iterates over each relationship type (belongs_to, has_many) and for each relationship, it prepares a schema unless the relationship is excluded from expansion. If the ‘expand’ option is true, it changes the schema to include type and id properties inside the ‘meta’ property.

Examples:

schema = generator.generate(expand: true, relationships_to_exclude_from_expansion: [:some_relationship])

Parameters:

  • relationships_to_exclude_from_expansion (Array) (defaults to: [])

    The relationships to exclude from expansion.

  • expand (Boolean) (defaults to: false)

    Whether to include the relationships of the related resource in the schema.

Returns:

  • (Hash)

    The generated schema.



33
34
35
36
37
38
39
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
68
69
70
71
72
# File 'lib/schemable/relationship_schema_generator.rb', line 33

def generate(relationships_to_exclude_from_expansion: [], expand: false)
  return {} if @relationships.blank? || @relationships == { belongs_to: {}, has_many: {} }

  schema = {
    type: :object,
    properties: {}
  }

  %i[belongs_to has_many].each do |relation_type|
    @relationships[relation_type]&.each do |relation, definition|
      non_expanded_data_properties = {
        type: :object,
        properties: {
          meta: {
            type: :object,
            properties: {
              included: { type: :boolean, default: false }
            }
          }
        }
      }

      result = relation_type == :belongs_to ? generate_schema(definition.model_name) : generate_schema(definition.model_name, collection: true)

      result = non_expanded_data_properties if !expand || relationships_to_exclude_from_expansion.include?(definition.model_name)

      schema[:properties].merge!(relation => result)
    end
  end

  # Modify the schema to include additional response relations
  schema = @schema_modifier.add_properties(schema, @model_definition.additional_response_relations, 'properties')

  # Modify the schema to exclude response relations
  @model_definition.excluded_response_relations.each do |key|
    schema = @schema_modifier.delete_properties(schema, "properties.#{key}")
  end

  schema
end

#generate_schema(type_name, collection: false) ⇒ Hash

Generates the schema for a specific relationship. If the ‘collection’ option is true, it generates a schema for a ‘has_many’ relationship, otherwise it generates a schema for a ‘belongs_to’ relationship. The difference between the two is that ‘data’ is an array in the ‘has_many’ relationship and an object in the ‘belongs_to’ relationship.

Examples:

relationship_schema = generator.generate_schema('resource_type', collection: true)

Parameters:

  • type_name (String)

    The type of the related resource.

  • collection (Boolean) (defaults to: false)

    Whether the relationship is a ‘has_many’ relationship.

Returns:

  • (Hash)

    The generated schema for the relationship.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/schemable/relationship_schema_generator.rb', line 86

def generate_schema(type_name, collection: false)
  schema = if collection
             {
               type: :object,
               properties: {
                 data: {
                   type: :array,
                   items: {
                     type: :object,
                     properties: {
                       id: { type: :string },
                       type: { type: :string, default: type_name }
                     }
                   }
                 }
               }
             }
           else
             {
               type: :object,
               properties: {
                 data: {
                   type: :object,
                   properties: {
                     id: { type: :string },
                     type: { type: :string, default: type_name }
                   }
                 }
               }
             }
           end

  # Modify the schema to nullable if the relationship is in nullable
  is_relation_nullable = @model_definition.nullable_relationships.include?(type_name)

  return schema unless is_relation_nullable

  @schema_modifier.add_properties(schema, { nullable: true }, 'properties.data')
end