class Vines::Storage

Public Class Methods

defer(method) click to toggle source

Wrap a blocking IO method in a new method that pushes the original method onto EventMachine's thread pool using EM#defer. Storage classes implemented with blocking IO don't need to worry about threading or blocking the EventMachine reactor thread if they wrap their methods with this one.

For example: def find_user(jid)

some_blocking_lookup(jid)

end defer :find_user

Storage classes that use asynchronous IO (through an EventMachine enabled library like em-http-request or em-redis) don't need any special consideration and must not use this method.

# File lib/vines/storage.rb, line 35
def self.defer(method)
  old = instance_method(method)
  define_method method do |*args|
    fiber = Fiber.current
    op = operation { old.bind(self).call(*args) }
    cb = proc {|result| fiber.resume(result) }
    EM.defer(op, cb)
    Fiber.yield
  end
end
fiber(method) click to toggle source

Wrap a method with Fiber yield and resume logic. The method must yield its result to a block. This makes it easier to write asynchronous implementations of authenticate, find_user, and save_user that block and return a result rather than yielding.

For example: def find_user(jid)

http = EM::HttpRequest.new(url).get
http.callback { yield build_user_from_http_response(http) }

end fiber :find_user

Because find_user has been wrapped in Fiber logic, we can call it synchronously even though it uses asynchronous EventMachine calls.

user = storage.find_user('alice@wonderland.lit') puts user.nil?

# File lib/vines/storage.rb, line 63
def self.fiber(method)
  old = instance_method(method)
  define_method method do |*args|
    fiber, yielding = Fiber.current, true
    old.bind(self).call(*args) do |user|
      fiber.resume(user) rescue yielding = false
    end
    Fiber.yield if yielding
  end
end
from_name(name, &block) click to toggle source
# File lib/vines/storage.rb, line 15
def self.from_name(name, &block)
  klass = @@nicks[name.to_sym]
  raise "#{name} storage class not found" unless klass
  klass.new(&block)
end
register(name) click to toggle source

Register a nickname that can be used in the config file to specify this storage implementation.

# File lib/vines/storage.rb, line 11
def self.register(name)
  @@nicks[name.to_sym] = self
end

Public Instance Methods

authenticate(username, password) click to toggle source

Validate the username and password pair and return a Vines::User object on success. Return nil on failure.

For example: user = storage.authenticate('alice@wonderland.lit', 'secr3t') puts user.nil?

This default implementation validates the password against a bcrypt hash of the password stored in the database. Sub-classes not using bcrypt passwords must override this method.

# File lib/vines/storage.rb, line 84
def authenticate(username, password)
  user = find_user(username)
  hash = BCrypt::Password.new(user.password) rescue nil
  (hash && hash == password) ? user : nil
end
find_fragment(jid, node) click to toggle source

Return the Nokogiri::XML::Node for the XML fragment stored for this JID. Return nil if the fragment could not be found. JID may be nil, a String, or a Vines::JID object. It may be a bare JID or a full JID. Implementations of this method must convert the JID to a bare JID before searching for the fragment in the database.

Private XML storage uniquely identifies fragments by JID, root element name, and root element namespace.

root = Nokogiri::XML('<custom xmlns=“urn:custom:ns”/>').root fragment = storage.find_fragment('alice@wonderland.lit', root) puts fragment.nil?

# File lib/vines/storage.rb, line 148
def find_fragment(jid, node)
  raise 'subclass must implement'
end
find_user(jid) click to toggle source

Return the Vines::User associated with the JID. Return nil if the user could not be found. JID may be nil, a String, or a Vines::JID object. It may be a bare JID or a full JID. Implementations of this method must convert the JID to a bare JID before searching for the user in the database.

user = storage.find_user('alice@wonderland.lit') puts user.nil?

# File lib/vines/storage.rb, line 98
def find_user(jid)
  raise 'subclass must implement'
end
find_vcard(jid) click to toggle source

Return the Nokogiri::XML::Node for the vcard stored for this JID. Return nil if the vcard could not be found. JID may be nil, a String, or a Vines::JID object. It may be a bare JID or a full JID. Implementations of this method must convert the JID to a bare JID before searching for the vcard in the database.

card = storage.find_vcard('alice@wonderland.lit') puts card.nil?

# File lib/vines/storage.rb, line 120
def find_vcard(jid)
  raise 'subclass must implement'
end
save_fragment(jid, fragment) click to toggle source

Save the XML fragment to the database and return when the save is complete. JID may be a String or a Vines::JID object. It may be a bare JID or a full JID. Implementations of this method must convert the JID to a bare JID before saving the fragment. Fragment is a Nokogiri::XML::Node object.

fragment = Nokogiri::XML('<custom xmlns=“urn:custom:ns”>some data</custom>').root storage.save_fragment('alice@wonderland.lit', fragment) puts 'saved'

# File lib/vines/storage.rb, line 160
def save_fragment(jid, fragment)
  raise 'subclass must implement'
end
save_user(user) click to toggle source

Persist the Vines::User object to the database and return when the save is complete.

alice = Vines::User.new(:jid => 'alice@wonderland.lit') storage.save_user(alice) puts 'saved'

# File lib/vines/storage.rb, line 108
def save_user(user)
  raise 'subclass must implement'
end
save_vcard(jid, card) click to toggle source

Save the vcard to the database and return when the save is complete. JID may be a String or a Vines::JID object. It may be a bare JID or a full JID. Implementations of this method must convert the JID to a bare JID before saving the vcard. Card is a Nokogiri::XML::Node object.

card = Nokogiri::XML('<vCard>…</vCard>').root storage.save_vcard('alice@wonderland.lit', card) puts 'saved'

# File lib/vines/storage.rb, line 132
def save_vcard(jid, card)
  raise 'subclass must implement'
end

Private Instance Methods

empty?(*args) click to toggle source

Return true if any of the arguments are nil or empty strings. For example: username, password = 'alice@wonderland.lit', '' empty?(username, password) #=> true

# File lib/vines/storage.rb, line 170
def empty?(*args)
  args.flatten.any? {|arg| (arg || '').strip.empty? }
end
operation() { || ... } click to toggle source

Return a proc suitable for running on the EM.defer thread pool that traps and logs any errors thrown by the provided block.

# File lib/vines/storage.rb, line 176
def operation
  proc do
    begin
      yield
    rescue => e
      log.error("Thread pool operation failed: #{e.message}")
      nil
    end
  end
end