Module: ApiKit::RailsApp

Defined in:
lib/api_kit/rails_app.rb

Constant Summary collapse

API_PAGINATE_METHODS_MAPPING =
{
  meta: :api_meta,
  links: :api_pagination,
  fields: :api_fields,
  include: :api_include,
  params: :api_serializer_params
}
API_METHODS_MAPPING =
{
  meta: :api_meta,
  fields: :api_fields,
  include: :api_include,
  params: :api_serializer_params
}

Class Method Summary collapse

Class Method Details

.add_errors_renderer!NilClass

Adds the error renderer

Returns:

  • (NilClass)


38
39
40
41
42
43
44
45
46
47
# File 'lib/api_kit/rails_app.rb', line 38

def self.add_errors_renderer!
  ActionController::Renderers.add(:api_errors) do |resource, options|
    self.content_type ||= Mime[:json]

    many = ApiKit::RailsApp.is_collection?(resource, options[:is_collection])
    resource = [ resource ] unless many

    ApiKit::ErrorSerializer.new(resource, options).to_json
  end
end

.add_renderer!NilClass

Adds the default renderer

Returns:

  • (NilClass)


52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
# File 'lib/api_kit/rails_app.rb', line 52

def self.add_renderer!
  ActionController::Renderers.add(:api_paginate) do |resource, options|
    self.content_type ||= Mime[:json]

    result = {}
    API_PAGINATE_METHODS_MAPPING.to_a[0..1].each do |opt, method_name|
      next unless respond_to?(method_name, true)
      result[opt] ||= send(method_name, resource)
    end

    # If it's an empty collection, return it directly.
    many = ApiKit::RailsApp.is_collection?(resource, options[:is_collection])

    API_PAGINATE_METHODS_MAPPING.to_a[2..-1].each do |opt, method_name|
      options[opt] ||= send(method_name) if respond_to?(method_name, true)
    end

    if options[:serializer_class]
      serializer_class = options[:serializer_class]
    else
      serializer_class = ApiKit::RailsApp.serializer_class(resource, many)
    end

    options[:fields] = api_fields(serializer_class, ApiKit::RailsApp.fetch_name(many, resource))
    options[:adapter] = :attributes
    options[:each_serializer] = serializer_class
    data = ActiveModelSerializers::SerializableResource.new(resource, options).as_json
    result[:data] = data
    result.to_json
  end

  ActionController::Renderers.add(:api) do |resource, options|
    self.content_type ||= Mime[:json]

    result = {}
    API_METHODS_MAPPING.to_a[0..0].each do |opt, method_name|
      next unless respond_to?(method_name, true)
      result[opt] ||= send(method_name, resource)
    end

    # If it's an empty collection, return it directly.
    many = ApiKit::RailsApp.is_collection?(resource, options[:is_collection])

    API_METHODS_MAPPING.to_a[1..-1].each do |opt, method_name|
      options[opt] ||= send(method_name) if respond_to?(method_name, true)
    end

    if options[:serializer_class]
      serializer_class = options[:serializer_class]
    else
      serializer_class = ApiKit::RailsApp.serializer_class(resource, many)
    end

    # Use Active Model Serializers properly with fallback
    options[:fields] = api_fields(serializer_class, ApiKit::RailsApp.fetch_name(many, resource))
    options[:adapter] = :attributes
    options[:each_serializer] = serializer_class
    if many
      data = ActiveModelSerializers::SerializableResource.new(resource, options).as_json
    else
      data = ActiveModelSerializers::SerializableResource.new([ resource ], options).as_json[0]
    end
    result[:data] = data
    result.to_json
  end
end

.fetch_name(many, resource) ⇒ String?

Resolves the singular model name for sparse fieldsets

Parameters:

  • many (Boolean)

    indicates whether the resource is a collection

  • resource (Object)

    serialized resource or collection

Returns:

  • (String, nil)

    singular model name when available



145
146
147
148
149
150
151
152
153
154
155
# File 'lib/api_kit/rails_app.rb', line 145

def self.fetch_name(many, resource)
  if many
    if resource.is_a?(ActiveRecord::Relation)
      resource&.model_name&.singular
    else
      resource.first&.model_name&.singular
    end
  else
    resource&.model_name&.singular
  end
end

.install!NilClass

Updates the mime types and registers the renderers

Returns:

  • (NilClass)


24
25
26
27
28
29
30
31
32
# File 'lib/api_kit/rails_app.rb', line 24

def self.install!
  return unless defined?(::Rails)

  parser = ActionDispatch::Request.parameter_parsers[:json]
  ActionDispatch::Request.parameter_parsers[:api] = parser

  self.add_renderer!
  self.add_errors_renderer!
end

.is_collection?(resource, force_is_collection = nil) ⇒ TrueClass

Checks if an object is a collection

Parameters:

  • resource (Object)

    to check

  • force_is_collection (NilClass) (defaults to: nil)

    flag to overwrite

Returns:

  • (TrueClass)

    upon success



124
125
126
127
128
# File 'lib/api_kit/rails_app.rb', line 124

def self.is_collection?(resource, force_is_collection = nil)
  return force_is_collection unless force_is_collection.nil?

  resource.respond_to?(:size) && !resource.respond_to?(:each_pair)
end

.serializer_class(resource, is_collection) ⇒ Class

Resolves resource serializer class

Returns:

  • (Class)


133
134
135
136
137
138
# File 'lib/api_kit/rails_app.rb', line 133

def self.serializer_class(resource, is_collection)
  klass = resource.class
  klass = resource.first.class if is_collection

  "#{klass.name}Serializer".constantize
end