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.

Examples

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.

Returns nothing.

# File lib/vines/storage.rb, line 42
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.

Examples

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?

Returns nothing.

# File lib/vines/storage.rb, line 73
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 19
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.

name - The String name for this storage backend.

Returns nothing.

# File lib/vines/storage.rb, line 15
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.

username - The String login JID to verify. password - The String password the user presented to the server.

Examples

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.

Returns a Vines::User object on success, nil on failure.

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

Delete a offline message from database.

id - The identifier of the offline message

Returns nothing.

# File lib/vines/storage.rb, line 242
def destroy_message(id)
  raise 'subclass must implement'
end
find_avatar_by_jid(jid) click to toggle source

Retrieve the avatar url by jid.

jid - The String or JID of the user.

Returns string

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

Find the private XML fragment previously stored by the user. Private XML storage uniquely identifies fragments by JID, root element name, and root element namespace.

jid - The String or JID of the user, possibly nil. This may be either a

bare JID or full JID. Implementations of this method must convert
the JID to a bare JID before searching for the fragment in the database.

node - The XML::Node that uniquely identifies the fragment by element

name and namespace.

Examples

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

Returns the fragment's Nokogiri::XML::Node or nil if not found.

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

Check whether offline messages are available for the user jid - The String or JID of the user, possibly nil. This may be

either a bare JID or full JID. Implementations of this method
must convert the JID to a bare JID before searching for
offline messages.

Returns hash

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

Find the user in the storage database by their unique JID.

jid - The String or JID of the user, possibly nil. This may be either a

bare JID or full JID. Implementations of this method must convert
the JID to a bare JID before searching for the user in the database.

Examples

# Bare JID lookup.
user = storage.find_user('alice@wonderland.lit')
puts user.nil?

# Full JID lookup.
user = storage.find_user('alice@wonderland.lit/tea')
puts user.nil?

Returns the User identified by the JID, nil if not found.

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

Find the user's vcard by their unique JID.

jid - The String or JID of the user, possibly nil. This may be either a

bare JID or full JID. Implementations of this method must convert
the JID to a bare JID before searching for the vcard in the database.

Examples

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

Returns the vcard's Nokogiri::XML::Node, nil if not found.

# File lib/vines/storage.rb, line 153
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 - The String or JID of the user, possibly nil. This may be

either a bare JID or full JID. Implementations of this method
must convert the JID to a bare JID before searching for the
fragment.

fragment - The XML::Node to save.

Examples

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

Returns nothing.

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

Save the offline message to the database, and return when the save is complete.

from - The String or JID of the user. to - The String or JID of the user. message - The message you want to store.

Returns nothing.

# File lib/vines/storage.rb, line 233
def save_message(from, to, message)
  raise 'subclass must implement'
end
save_user(user) click to toggle source

Persist the user to the database, and return when the save is complete.

user - The User to persist.

Examples

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

Returns nothing.

# File lib/vines/storage.rb, line 137
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 - The String or JID of the user, possibly nil. This may be either a

bare JID or full JID. Implementations of this method must convert
the JID to a bare JID before saving the vcard.

card - The vcard's Nokogiri::XML::Node.

Examples

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

Returns nothing.

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

Private Instance Methods

empty?(*args) click to toggle source

Determine if any of the arguments are nil or empty strings.

Examples

username, password = 'alice@wonderland.lit', ''
empty?(username, password) #=> true

Returns true if any of the arguments are nil or empty strings.

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

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

block - The block to wrap in error handling.

Examples

op = operation { do_something_on_thread_pool() }
EM.defer(op)

Returns a Proc.

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