Class: Apiwork::Adapter::Base

Inherits:
Object
  • Object
show all
Includes:
Configurable
Defined in:
lib/apiwork/adapter/base.rb

Overview

Base class for adapters.

The engine of an API. Handles both introspection (generating types from representations) and runtime (processing requests through capabilities, serializing, and wrapping responses). The class declaration acts as a manifest.

Examples:

class MyAdapter < Apiwork::Adapter::Base
  adapter_name :my

  resource_serializer Serializer::Resource::Default
  error_serializer Serializer::Error::Default

  member_wrapper Wrapper::Member::Default
  collection_wrapper Wrapper::Collection::Default
  error_wrapper Wrapper::Error::Default

  capability Capability::Filtering
  capability Capability::Pagination
end

Direct Known Subclasses

Standard

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Configurable

define, option

Class Method Details

.adapter_name(value = nil) ⇒ Symbol?

The adapter name for this adapter.

Examples:

adapter_name :my

Parameters:

  • value (Symbol, String, nil) (defaults to: nil)

    (nil) The adapter name.

Returns:

  • (Symbol, nil)


39
40
41
42
# File 'lib/apiwork/adapter/base.rb', line 39

def adapter_name(value = nil)
  @adapter_name = value.to_sym if value
  @adapter_name
end

.capabilitiesObject



81
82
83
84
85
86
# File 'lib/apiwork/adapter/base.rb', line 81

def capabilities
  inherited = superclass.respond_to?(:capabilities) ? superclass.capabilities : []
  skipped = @skipped_capabilities || []
  all = (inherited + (@capabilities || [])).uniq
  all.reject { |capability| skipped.include?(capability.capability_name) }
end

.capability(klass) ⇒ void

This method returns an undefined value.

Registers a capability for this adapter.

Capabilities are self-contained concerns (pagination, filtering, etc.) that handle both introspection and runtime behavior.

Examples:

capability Capability::Filtering
capability Capability::Pagination

Parameters:



57
58
59
60
61
62
63
64
65
# File 'lib/apiwork/adapter/base.rb', line 57

def capability(klass)
  @capabilities ||= []
  @capabilities << klass

  return unless klass.options.any?

  name = klass.capability_name
  options[name] = Configuration::Option.new(name, :hash, children: klass.options)
end

.collection_wrapper(klass = nil) ⇒ Class<Wrapper::Collection::Base>?

Sets the wrapper class for collection responses.

Examples:

collection_wrapper Wrapper::Collection::Default

Parameters:

Returns:



148
149
150
151
152
153
154
# File 'lib/apiwork/adapter/base.rb', line 148

def collection_wrapper(klass = nil)
  if klass
    validate_class_setter!(:collection_wrapper, klass, Wrapper::Collection::Base, 'Wrapper')
    @collection_wrapper = klass
  end
  @collection_wrapper || (superclass.respond_to?(:collection_wrapper) && superclass.collection_wrapper)
end

.error_serializer(klass = nil) ⇒ Class<Serializer::Error::Base>?

Sets the serializer class for errors.

Examples:

error_serializer Serializer::Error::Default

Parameters:

Returns:



114
115
116
117
118
119
120
# File 'lib/apiwork/adapter/base.rb', line 114

def error_serializer(klass = nil)
  if klass
    validate_class_setter!(:error_serializer, klass, Serializer::Error::Base, 'Serializer')
    @error_serializer = klass
  end
  @error_serializer || (superclass.respond_to?(:error_serializer) && superclass.error_serializer)
end

.error_wrapper(klass = nil) ⇒ Class<Wrapper::Error::Base>?

Sets the wrapper class for error responses.

Examples:

error_wrapper Wrapper::Error::Default

Parameters:

Returns:



165
166
167
168
169
170
171
# File 'lib/apiwork/adapter/base.rb', line 165

def error_wrapper(klass = nil)
  if klass
    validate_class_setter!(:error_wrapper, klass, Wrapper::Error::Base, 'Wrapper')
    @error_wrapper = klass
  end
  @error_wrapper || (superclass.respond_to?(:error_wrapper) && superclass.error_wrapper)
end

.member_wrapper(klass = nil) ⇒ Class<Wrapper::Member::Base>?

Sets the wrapper class for single-record responses.

Examples:

member_wrapper Wrapper::Member::Default

Parameters:

Returns:



131
132
133
134
135
136
137
# File 'lib/apiwork/adapter/base.rb', line 131

def member_wrapper(klass = nil)
  if klass
    validate_class_setter!(:member_wrapper, klass, Wrapper::Member::Base, 'Wrapper')
    @member_wrapper = klass
  end
  @member_wrapper || (superclass.respond_to?(:member_wrapper) && superclass.member_wrapper)
end

.resource_serializer(klass = nil) ⇒ Class<Serializer::Resource::Base>?

Sets the serializer class for records and collections.

Examples:

resource_serializer Serializer::Resource::Default

Parameters:

Returns:



97
98
99
100
101
102
103
# File 'lib/apiwork/adapter/base.rb', line 97

def resource_serializer(klass = nil)
  if klass
    validate_class_setter!(:resource_serializer, klass, Serializer::Resource::Base, 'Serializer')
    @resource_serializer = klass
  end
  @resource_serializer || (superclass.respond_to?(:resource_serializer) && superclass.resource_serializer)
end

.skip_capability(name) ⇒ void

This method returns an undefined value.

Skips an inherited capability by name.

Examples:

skip_capability :pagination

Parameters:

  • name (Symbol)

    The capability name to skip.



76
77
78
79
# File 'lib/apiwork/adapter/base.rb', line 76

def skip_capability(name)
  @skipped_capabilities ||= []
  @skipped_capabilities << name.to_sym
end

Instance Method Details

#apply_request_transformers(request, phase:) ⇒ Object



225
226
227
# File 'lib/apiwork/adapter/base.rb', line 225

def apply_request_transformers(request, phase:)
  run_capability_request_transformers(request, phase:)
end

#apply_response_transformers(response) ⇒ Object



229
230
231
# File 'lib/apiwork/adapter/base.rb', line 229

def apply_response_transformers(response)
  run_capability_response_transformers(response)
end

#capabilitiesObject



233
234
235
236
237
# File 'lib/apiwork/adapter/base.rb', line 233

def capabilities
  @capabilities ||= self.class.capabilities.map do |klass|
    klass.new({}, adapter_name: self.class.adapter_name)
  end
end

#process_collection(collection, representation_class, request, context: {}, meta: {}) ⇒ Object



189
190
191
192
193
# File 'lib/apiwork/adapter/base.rb', line 189

def process_collection(collection, representation_class, request, context: {}, meta: {})
  collection, , serialize_options = apply_capabilities(collection, representation_class, request, wrapper_type: :collection)
  data = self.class.resource_serializer.serialize(representation_class, collection, context:, serialize_options:)
  self.class.collection_wrapper.wrap(data, , representation_class.root_key, meta)
end

#process_error(error, representation_class, context: {}) ⇒ Object



201
202
203
204
# File 'lib/apiwork/adapter/base.rb', line 201

def process_error(error, representation_class, context: {})
  data = self.class.error_serializer.serialize(error, context:)
  self.class.error_wrapper.wrap(data)
end

#process_member(record, representation_class, request, context: {}, meta: {}) ⇒ Object



195
196
197
198
199
# File 'lib/apiwork/adapter/base.rb', line 195

def process_member(record, representation_class, request, context: {}, meta: {})
  record, , serialize_options = apply_capabilities(record, representation_class, request, wrapper_type: :member)
  data = self.class.resource_serializer.serialize(representation_class, record, context:, serialize_options:)
  self.class.member_wrapper.wrap(data, , representation_class.root_key, meta)
end

#register_api(api_class) ⇒ Object



206
207
208
209
210
211
212
213
# File 'lib/apiwork/adapter/base.rb', line 206

def register_api(api_class)
  capabilities.each do |capability|
    capability.api_types(api_class)
  end

  error_serializer_class = self.class.error_serializer
  error_serializer_class.new.api_types(api_class)
end

#register_contract(contract_class, representation_class, resource_actions: {}) ⇒ Object



215
216
217
218
219
220
221
222
223
# File 'lib/apiwork/adapter/base.rb', line 215

def register_contract(contract_class, representation_class, resource_actions: {})
  capabilities.each do |capability|
    capability.contract_types(contract_class, representation_class, resource_actions)
  end

  self.class.resource_serializer.new(representation_class).contract_types(contract_class)

  build_action_responses(contract_class, representation_class, resource_actions) if resource_actions.any?
end