Class: Perry::Adapters::AbstractAdapter

Inherits:
Object
  • Object
show all
Includes:
Logger
Defined in:
lib/perry/adapters/abstract_adapter.rb

Overview

Perry::Adapters::AbstractAdapter

This is the base class from which all adapters should inherit from. Subclasses should overwrite one or all of read, write, and/or delete. They should also register themselves with a unique name using the register_as class method.

Adapters contain a stack of code that is executed on each request. Here is a diagram of the basic anatomy of the adaper stack:

+----------------+
|   Perry::Base  |
+----------------+
        |
+----------------+
|   Processors   |
+----------------+
        |
+----------------+
|   ModelBridge  |
+----------------+
        |
+----------------+
|   Middlewares  |
+----------------+
        |
+----------------+
|     Adapter    |
+----------------+

Each request is routed through registred processors, the ModelBridge, and registered middlewares before reaching the adapter. After the adapter does its operation the return value passes through each item in the stack allowing stack items to do both custom pre and post processing to every request.

Configuration

You can configure your adapters using the configure method on Perry::Base

configure(:read) do |config|
  config.adapter_var_1 = :custom_value
  config.adapter_var_2 = [:some, :values]
end

This block creates a new configuration context. Each context is merged onto the previous context allowing subclasses to override configuration set by their parent class.

Middlewares

Middlewares allow you to add custom logic between the model and the adapter. A good example is caching. A caching middleware could be implemented that intercepted a request to the adapter and returned the cached value for that request. If the request is a cache miss it could pass the request on to the adapter, and then cache the result for subsequent calls of the same request.

This is an example mof a no-op middleware:

class NoOpMiddleware

  def initialize(adapter, config={})
    @adapter = adapter
    @config = config
  end

  def call(options)
    @adapter.call(options)
  end

end

Though this doesn’t do anything it serves to demonstrate the basic structure of a middleware. Logic could be added to perform caching, custom querying, or custom result processing.

Middlewares can also be chained to perform several independent actions. Middlewares are configured through a custom configuration method:

configure(:read) do |config|
  config.add_middleware(MyMiddleware, :config => 'var', :foo => 'bar')
end

ModelBridge

The ModelBridge is simply a middleware that is always installed. It instantiates the records from the data returned by the adapter. It “bridges” the raw data to the mapped object.

Processors

Much like middlewares, processors allow you to insert logic into the request stack. The differentiation is that processors are able to manipulate the instantiated objects rather than just the raw data. Processors have access to the objects immediately before passing the data back to the model space.

The interface for a processor is identical to that of a middleware. The return value of the call to adapter; however, is an array of Perry::Base objects rather than Hashes of attributes.

Configuration is also very similar to middlewares:

configure(:read) do |config|
  config.add_processor(MyProcessor, :config => 'var', :foo => 'bar')
end

Direct Known Subclasses

BERTRPCAdapter, RestfulHTTPAdapter

Defined Under Namespace

Classes: AdapterConfig

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Logger

included

Constructor Details

#initialize(type, config) ⇒ AbstractAdapter

Accepts type as :read, :write, or :delete and a base configuration context for this adapter.



112
113
114
115
# File 'lib/perry/adapters/abstract_adapter.rb', line 112

def initialize(type, config)
  @type = type.to_sym
  @configuration_contexts = config.is_a?(Array) ? config : [config]
end

Instance Attribute Details

#configObject

return the merged configuration object



131
132
133
# File 'lib/perry/adapters/abstract_adapter.rb', line 131

def config
  @config
end

#typeObject (readonly)

Returns the value of attribute type.



108
109
110
# File 'lib/perry/adapters/abstract_adapter.rb', line 108

def type
  @type
end

Class Method Details

.create(type, config) ⇒ Object

Wrapper to the standard init method that will lookup the adapter’s class based on its registered symbol name.



119
120
121
122
# File 'lib/perry/adapters/abstract_adapter.rb', line 119

def self.create(type, config)
  klass = @@registered_adapters[type.to_sym]
  klass.new(type, config)
end

.register_as(name) ⇒ Object

New adapters should register themselves using this method



187
188
189
# File 'lib/perry/adapters/abstract_adapter.rb', line 187

def self.register_as(name)
  @@registered_adapters[name.to_sym] = self
end

Instance Method Details

#call(mode, options) ⇒ Object

runs the adapter in the specified type mode – designed to work with the middleware stack



136
137
138
139
140
141
142
143
144
145
146
# File 'lib/perry/adapters/abstract_adapter.rb', line 136

def call(mode, options)
  @stack ||= self.stack_items.inject(self.method(:execute)) do |below, (above_klass, above_config)|
    above_klass.new(below, above_config)
  end

  options[:mode] = mode.to_sym
  if options[:relation] && options[:relation].modifiers_value[:noop]
    options[:noop] = options[:relation].modifiers_value[:noop]
  end
  @stack.call(options)
end

#delete(options) ⇒ Object

Abstract delete method – overridden by subclasses

Raises:

  • (NotImplementedError)


180
181
182
183
184
# File 'lib/perry/adapters/abstract_adapter.rb', line 180

def delete(options)
  raise(NotImplementedError,
        "You must not use the abstract adapter.  Implement an adapter that extends the " +
        "Perry::Adapters::AbstractAdapter class and overrides this method.")
end

#execute(options) ⇒ Object

Proxy method for the stack and the actual adapter action. This method passes the call on to the appropriate method based on options unless the query has a :noop modifier in which case it returns nil.



161
162
163
# File 'lib/perry/adapters/abstract_adapter.rb', line 161

def execute(options)
  self.send(options[:mode], options) unless options[:noop]
end

#extend_adapter(config) ⇒ Object

Return a new adapter of the same type that adds the given configuration context



125
126
127
128
# File 'lib/perry/adapters/abstract_adapter.rb', line 125

def extend_adapter(config)
  config = config.is_a?(Array) ? config : [config]
  self.class.create(self.type, @configuration_contexts + config)
end

#middlewaresObject

Return an array of added middlewares



149
150
151
# File 'lib/perry/adapters/abstract_adapter.rb', line 149

def middlewares
  self.config[:middlewares] || []
end

#processorsObject

Return an array of added processors



154
155
156
# File 'lib/perry/adapters/abstract_adapter.rb', line 154

def processors
  self.config[:processors] || []
end

#read(options) ⇒ Object

Abstract read method – overridden by subclasses

Raises:

  • (NotImplementedError)


166
167
168
169
170
# File 'lib/perry/adapters/abstract_adapter.rb', line 166

def read(options)
  raise(NotImplementedError,
        "You must not use the abstract adapter.  Implement an adapter that extends the " +
        "Perry::Adapters::AbstractAdapter class and overrides this method.")
end

#write(options) ⇒ Object

Abstract write method – overridden by subclasses

Raises:

  • (NotImplementedError)


173
174
175
176
177
# File 'lib/perry/adapters/abstract_adapter.rb', line 173

def write(options)
  raise(NotImplementedError,
        "You must not use the abstract adapter.  Implement an adapter that extends the " +
        "Perry::Adapters::AbstractAdapter class and overrides this method.")
end