Class: Horcrux::Multiple

Inherits:
Object
  • Object
show all
Includes:
Methods
Defined in:
lib/horcrux/multiple.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Methods

#fetch, included, #key_for

Constructor Details

#initialize(*adapters) ⇒ Multiple

Sets up an Adapter using a collection of other adapters. The first is assumed to be the main, while the others are write-through caches. This is good for caching.

mysql = Horcrux::MysqlAdapter.new ... # fake
memcache = Horcrux::MemcacheAdapter.new ... # fake
adapter = Horcrux::Multiple.new mysql, memcache

Reads will hit the secondary adapters before the main. Writes will hit the main adapter first, before being sent to the secondary adapters.

*adapters - One or more Horcrux-compliant adapters.



20
21
22
23
24
25
26
27
28
29
30
# File 'lib/horcrux/multiple.rb', line 20

def initialize(*adapters)
  if adapters.empty?
    raise ArgumentError, "Need at least 1 adapter."
  end

  @main = adapters.shift
  @adapters = adapters
  @error_handlers = []
  @missing_handlers = []
  @rescuable_exceptions = [StandardError]
end

Instance Attribute Details

#error_handlersObject (readonly)

Returns the value of attribute error_handlers.



6
7
8
# File 'lib/horcrux/multiple.rb', line 6

def error_handlers
  @error_handlers
end

#missing_handlersObject (readonly)

Returns the value of attribute missing_handlers.



6
7
8
# File 'lib/horcrux/multiple.rb', line 6

def missing_handlers
  @missing_handlers
end

#rescuable_exceptionsObject (readonly)

Returns the value of attribute rescuable_exceptions.



5
6
7
# File 'lib/horcrux/multiple.rb', line 5

def rescuable_exceptions
  @rescuable_exceptions
end

Instance Method Details

#call_adapter(adapter, method, *args) ⇒ Object

Calls the given adapter, swallowing up any error.

adapter - A Horcrux adapter. method - A Symbol identifying the method to call. *args - One or more arguments to send to the methods.

Returns the value of the method call.



148
149
150
151
152
153
154
155
156
157
158
# File 'lib/horcrux/multiple.rb', line 148

def call_adapter(adapter, method, *args)
  adapter.send(method, *args)
rescue Object => err
  raise unless @rescuable_exceptions.any? { |klass| err.is_a?(klass) }

  if @error_handlers.each do |handler|
    handler.call err, :adapter => adapter, :method => method, :args => args
  end.empty?
    $stderr.puts "#{err.class} Exception for #{adapter.inspect}##{method}: #{err}"
  end
end

#call_missing_handlers(values, missing) ⇒ Object

Call the on_missing callbacks for the handlers that were missing keys. This gives you a chance to set those values in the secondary adapters.

values - A Hash of all of the found keys => values. missing - A Hash of Adapter => Array of missing keys.

Returns nothing.



191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/horcrux/multiple.rb', line 191

def call_missing_handlers(values, missing)
  return if @missing_handlers.empty?
  missing.each do |adapter, keys|
    missing_values = {}
    keys.each do |key|
      missing_values[key] = values[key]
    end

    @missing_handlers.each do |handler|
      handler.call adapter, missing_values
    end
  end
end

#delete(key) ⇒ Object



102
103
104
# File 'lib/horcrux/multiple.rb', line 102

def delete(key)
  write_through :delete, key
end

#delete_all(*keys) ⇒ Object



106
107
108
# File 'lib/horcrux/multiple.rb', line 106

def delete_all(*keys)
  write_through :delete_all, *keys
end

#get(key) ⇒ Object



90
91
92
# File 'lib/horcrux/multiple.rb', line 90

def get(key)
  read_cache :get, key
end

#get_all(*keys) ⇒ Object

HORCRUX METHODS



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/horcrux/multiple.rb', line 62

def get_all(*keys)
  original = keys.dup
  adapter_missing = {}
  values = {}

  @adapters.each do |adapter|
    found, missing = get_from_adapter(adapter, keys)
    values.update(found)

    if !missing.empty?
      adapter_missing[adapter] = missing
    end

    keys = missing
  end

  found, missing = get_from_adapter(@main, keys)
  values.update(found)

  call_missing_handlers(values, adapter_missing)

  original.map { |key| values[key] }
end

#get_from_adapter(adapter, keys) ⇒ Object

Gets all keys from the adapter.

adapter - A Horcrux adapter. keys - Array of String keys to fetch.

Returns an Array tuple with a Hash of found keys/values, and an Array of missing keys.



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/horcrux/multiple.rb', line 167

def get_from_adapter(adapter, keys)
  missing = []
  found = {}

  adapter.get_all(*keys).each_with_index do |value, index|
    key = keys[index]

    if value
      found[key] = value
    else
      missing << key
    end
  end unless keys.empty?

  [found, missing]
end

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


86
87
88
# File 'lib/horcrux/multiple.rb', line 86

def key?(key)
  read_cache :key?, key
end

#on_error(&block) ⇒ Object

Public: Adds the given block to the chain of handlers to call for a raised exception while accessing one of the adapters.

@adapter.on_error do |err, obj|
  obj[:adapter]
  obj[:method]
  obj[:args]
end

Returns nothing.



42
43
44
45
# File 'lib/horcrux/multiple.rb', line 42

def on_error(&block)
  @error_handlers << block
  nil
end

#on_missing(&block) ⇒ Object

Public: Adds the given block to the chain of handlers to call when a secondary adapter is missing one or more keys.

@adapter.on_missing do |adapter, values|
  adapter.set_all(values)
end

Returns nothing.



55
56
57
58
# File 'lib/horcrux/multiple.rb', line 55

def on_missing(&block)
  @missing_handlers << block
  nil
end

#read_cache(method, *args) ⇒ Object

Reads the data from the other adapters before the main adapter.

method - A Symbol identifying the method to call. *args - One or more arguments to send to the methods.

Returns the result of the first adapter to respond with a value.



133
134
135
136
137
138
139
# File 'lib/horcrux/multiple.rb', line 133

def read_cache(method, *args)
  value = nil
  @adapters.detect do |adapter| 
    value = call_adapter adapter, method, *args
  end
  value || @main.send(method, *args)
end

#set(key, value) ⇒ Object



94
95
96
# File 'lib/horcrux/multiple.rb', line 94

def set(key, value)
  write_through :set, key, value
end

#set_all(values) ⇒ Object



98
99
100
# File 'lib/horcrux/multiple.rb', line 98

def set_all(values)
  write_through :set_all, values
end

#write_through(method, *args) ⇒ Object

Writes the data to the main adapter first, and then to the other adapters.

method - A Symbol identifying the method to call. *args - One or more arguments to send to the methods.

Returns the result of the method call on the main adapter.



119
120
121
122
123
124
125
# File 'lib/horcrux/multiple.rb', line 119

def write_through(method, *args)
  result = @main.send(method, *args)
  @adapters.each do |adapter|
    call_adapter adapter, method, *args
  end
  result
end