vines-mongodb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +53 -0
- data/Rakefile +20 -0
- data/lib/vines/storage/mongodb.rb +132 -0
- data/spec/mock_mongo.rb +38 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/storage/mongodb_spec.rb +98 -0
- data/spec/storage_specs.rb +182 -0
- data/vines-mongodb.gemspec +24 -0
- metadata +143 -0
    
        data/Gemfile
    ADDED
    
    
    
        data/LICENSE
    ADDED
    
    | @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            Copyright (c) 2010-2013 Negative Code
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy
         | 
| 4 | 
            +
            of this software and associated documentation files (the "Software"), to deal
         | 
| 5 | 
            +
            in the Software without restriction, including without limitation the rights
         | 
| 6 | 
            +
            to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         | 
| 7 | 
            +
            copies of the Software, and to permit persons to whom the Software is
         | 
| 8 | 
            +
            furnished to do so, subject to the following conditions:
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            The above copyright notice and this permission notice shall be included in
         | 
| 11 | 
            +
            all copies or substantial portions of the Software.
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         | 
| 14 | 
            +
            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         | 
| 15 | 
            +
            FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         | 
| 16 | 
            +
            AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         | 
| 17 | 
            +
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         | 
| 18 | 
            +
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
         | 
| 19 | 
            +
            THE SOFTWARE.
         | 
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            # Welcome to Vines
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Vines is a scalable XMPP chat server, using EventMachine for asynchronous IO.
         | 
| 4 | 
            +
            This gem provides support for storing user data in
         | 
| 5 | 
            +
            [MongoDB](http://www.mongodb.org/).
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Additional documentation can be found at [getvines.org](http://www.getvines.org/).
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            ## Usage
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            ```
         | 
| 12 | 
            +
            $ gem install vines vines-mongodb
         | 
| 13 | 
            +
            $ vines init wonderland.lit
         | 
| 14 | 
            +
            $ cd wonderland.lit && vines start
         | 
| 15 | 
            +
            ```
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            ## Configuration
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            Add the following configuration block to a virtual host definition in
         | 
| 20 | 
            +
            the server's `conf/config.rb` file.
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            ```ruby
         | 
| 23 | 
            +
            storage 'mongodb' do
         | 
| 24 | 
            +
              host 'localhost', 27017
         | 
| 25 | 
            +
              host 'localhost', 27018 # optional, connects to replica set
         | 
| 26 | 
            +
              database 'xmpp'
         | 
| 27 | 
            +
              tls true
         | 
| 28 | 
            +
              username ''
         | 
| 29 | 
            +
              password ''
         | 
| 30 | 
            +
              pool 5
         | 
| 31 | 
            +
            end
         | 
| 32 | 
            +
            ```
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            ## Dependencies
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            Vines requires Ruby 1.9.3 or better. Instructions for installing the
         | 
| 37 | 
            +
            needed OS packages, as well as Ruby itself, are available at
         | 
| 38 | 
            +
            [getvines.org/ruby](http://www.getvines.org/ruby).
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            ## Development
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            ```
         | 
| 43 | 
            +
            $ script/bootstrap
         | 
| 44 | 
            +
            $ script/tests
         | 
| 45 | 
            +
            ```
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            ## Contact
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            * David Graham <david@negativecode.com>
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            ## License
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            Vines is released under the MIT license. Check the LICENSE file for details.
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            require 'rake'
         | 
| 2 | 
            +
            require 'rake/clean'
         | 
| 3 | 
            +
            require 'rake/testtask'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            CLOBBER.include('pkg')
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            directory 'pkg'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            desc 'Build distributable packages'
         | 
| 10 | 
            +
            task :build => [:pkg] do
         | 
| 11 | 
            +
              system 'gem build vines-mongodb.gemspec && mv vines-*.gem pkg/'
         | 
| 12 | 
            +
            end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            Rake::TestTask.new(:test) do |test|
         | 
| 15 | 
            +
              test.libs << 'spec'
         | 
| 16 | 
            +
              test.pattern = 'spec/**/*_spec.rb'
         | 
| 17 | 
            +
              test.warning = false
         | 
| 18 | 
            +
            end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            task :default => [:clobber, :test, :build]
         | 
| @@ -0,0 +1,132 @@ | |
| 1 | 
            +
            require 'mongo'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Vines
         | 
| 4 | 
            +
              class Storage
         | 
| 5 | 
            +
                class MongoDB < Storage
         | 
| 6 | 
            +
                  register :mongodb
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  %w[database tls username password pool].each do |name|
         | 
| 9 | 
            +
                    define_method(name) do |*args|
         | 
| 10 | 
            +
                      if args.first
         | 
| 11 | 
            +
                        @config[name.to_sym] = args.first
         | 
| 12 | 
            +
                      else
         | 
| 13 | 
            +
                        @config[name.to_sym]
         | 
| 14 | 
            +
                      end
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def initialize(&block)
         | 
| 19 | 
            +
                    @config, @hosts = {}, []
         | 
| 20 | 
            +
                    instance_eval(&block)
         | 
| 21 | 
            +
                    raise "Must provide database" unless @config[:database]
         | 
| 22 | 
            +
                    raise "Must provide at least one host connection" if @hosts.empty?
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def host(name, port)
         | 
| 26 | 
            +
                    pair = [name, port]
         | 
| 27 | 
            +
                    raise "duplicate hosts not allowed: #{name}:#{port}" if @hosts.include?(pair)
         | 
| 28 | 
            +
                    @hosts << pair
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def find_user(jid)
         | 
| 32 | 
            +
                    jid = JID.new(jid).bare.to_s
         | 
| 33 | 
            +
                    return if jid.empty?
         | 
| 34 | 
            +
                    doc = get(:users, jid)
         | 
| 35 | 
            +
                    return unless doc
         | 
| 36 | 
            +
                    User.new(jid: jid).tap do |user|
         | 
| 37 | 
            +
                      user.name, user.password = doc.values_at('name', 'password')
         | 
| 38 | 
            +
                      (doc['roster'] || {}).each_pair do |jid, props|
         | 
| 39 | 
            +
                        user.roster << Contact.new(
         | 
| 40 | 
            +
                          jid: jid,
         | 
| 41 | 
            +
                          name: props['name'],
         | 
| 42 | 
            +
                          subscription: props['subscription'],
         | 
| 43 | 
            +
                          ask: props['ask'],
         | 
| 44 | 
            +
                          groups: props['groups'] || [])
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  def save_user(user)
         | 
| 50 | 
            +
                    id = user.jid.bare.to_s
         | 
| 51 | 
            +
                    doc = get(:users, id) || {'_id' => id}
         | 
| 52 | 
            +
                    doc['name'] = user.name
         | 
| 53 | 
            +
                    doc['password'] = user.password
         | 
| 54 | 
            +
                    doc['roster'] = {}
         | 
| 55 | 
            +
                    user.roster.each do |contact|
         | 
| 56 | 
            +
                      doc['roster'][contact.jid.bare.to_s] = contact.to_h
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
                    save_doc(:users, doc)
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  def find_vcard(jid)
         | 
| 62 | 
            +
                    jid = JID.new(jid).bare.to_s
         | 
| 63 | 
            +
                    return if jid.empty?
         | 
| 64 | 
            +
                    doc = get(:vcards, jid)
         | 
| 65 | 
            +
                    return unless doc
         | 
| 66 | 
            +
                    Nokogiri::XML(doc['card']).root rescue nil
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  def save_vcard(jid, card)
         | 
| 70 | 
            +
                    jid = JID.new(jid).bare.to_s
         | 
| 71 | 
            +
                    doc = get(:vcards, jid) || {'_id' => jid}
         | 
| 72 | 
            +
                    doc['card'] = card.to_xml
         | 
| 73 | 
            +
                    save_doc(:vcards, doc)
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  def find_fragment(jid, node)
         | 
| 77 | 
            +
                    jid = JID.new(jid).bare.to_s
         | 
| 78 | 
            +
                    return if jid.empty?
         | 
| 79 | 
            +
                    doc = get(:fragments, fragment_id(jid, node))
         | 
| 80 | 
            +
                    return unless doc
         | 
| 81 | 
            +
                    Nokogiri::XML(doc['xml']).root rescue nil
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  def save_fragment(jid, node)
         | 
| 85 | 
            +
                    jid = JID.new(jid).bare.to_s
         | 
| 86 | 
            +
                    id = fragment_id(jid, node)
         | 
| 87 | 
            +
                    doc = get(:fragments, id) || {'_id' => id}
         | 
| 88 | 
            +
                    doc['xml'] = node.to_xml
         | 
| 89 | 
            +
                    save_doc(:fragments, doc)
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  private
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                  def fragment_id(jid, node)
         | 
| 95 | 
            +
                    id = Digest::SHA1.hexdigest("#{node.name}:#{node.namespace.href}")
         | 
| 96 | 
            +
                    "#{jid}:#{id}"
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                  def get(collection, id)
         | 
| 100 | 
            +
                    db.collection(collection).find_one({_id: id})
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
                  defer :get
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  def save_doc(collection, doc)
         | 
| 105 | 
            +
                    db.collection(collection).save(doc, safe: true)
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
                  defer :save_doc
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                  def db
         | 
| 110 | 
            +
                    @db ||= connect
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                  def connect
         | 
| 114 | 
            +
                    opts = {
         | 
| 115 | 
            +
                      pool_timeout: 5,
         | 
| 116 | 
            +
                      pool_size: @config[:pool] || 5,
         | 
| 117 | 
            +
                      ssl: @config[:tls]
         | 
| 118 | 
            +
                    }
         | 
| 119 | 
            +
                    conn = if @hosts.size == 1
         | 
| 120 | 
            +
                      Mongo::Connection.new(@hosts.first[0], @hosts.first[1], opts)
         | 
| 121 | 
            +
                    else
         | 
| 122 | 
            +
                      Mongo::ReplSetConnection.new(*@hosts, opts)
         | 
| 123 | 
            +
                    end
         | 
| 124 | 
            +
                    conn.db(@config[:database]).tap do |db|
         | 
| 125 | 
            +
                      user = @config[:username] || ''
         | 
| 126 | 
            +
                      pass = @config[:password] || ''
         | 
| 127 | 
            +
                      db.authenticate(user, pass) unless user.empty? || pass.empty?
         | 
| 128 | 
            +
                    end
         | 
| 129 | 
            +
                  end
         | 
| 130 | 
            +
                end
         | 
| 131 | 
            +
              end
         | 
| 132 | 
            +
            end
         | 
    
        data/spec/mock_mongo.rb
    ADDED
    
    | @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            # A mock implementation of the Mongo::Connection class.
         | 
| 2 | 
            +
            class MockMongo
         | 
| 3 | 
            +
              def initialize
         | 
| 4 | 
            +
                @collections = collections
         | 
| 5 | 
            +
              end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              def collection(name)
         | 
| 8 | 
            +
                @collections[name]
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              def clear
         | 
| 12 | 
            +
                @collections = collections
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              def collections
         | 
| 16 | 
            +
                {
         | 
| 17 | 
            +
                  users: MockCollection.new,
         | 
| 18 | 
            +
                  vcards: MockCollection.new,
         | 
| 19 | 
            +
                  fragments: MockCollection.new
         | 
| 20 | 
            +
                }
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              class MockCollection
         | 
| 24 | 
            +
                def initialize
         | 
| 25 | 
            +
                  @docs = {}
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def find_one(doc)
         | 
| 29 | 
            +
                  id = doc['_id'] || doc[:_id]
         | 
| 30 | 
            +
                  @docs[id]
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def save(doc, opts={})
         | 
| 34 | 
            +
                  id = doc['_id'] || doc[:_id]
         | 
| 35 | 
            +
                  @docs[id] = doc
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
            end
         | 
    
        data/spec/spec_helper.rb
    ADDED
    
    
| @@ -0,0 +1,98 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Vines::Storage::MongoDB do
         | 
| 4 | 
            +
              include StorageSpecs
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              MOCK_MONGO = MockMongo.new
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              before do
         | 
| 9 | 
            +
                fibered do
         | 
| 10 | 
            +
                  db = MOCK_MONGO
         | 
| 11 | 
            +
                  db.collection(:users).save({'_id' => 'empty@wonderland.lit'})
         | 
| 12 | 
            +
                  db.collection(:users).save({'_id' => 'no_password@wonderland.lit', 'foo' => 'bar'})
         | 
| 13 | 
            +
                  db.collection(:users).save({'_id' => 'clear_password@wonderland.lit', 'password' => 'secret'})
         | 
| 14 | 
            +
                  db.collection(:users).save({'_id' => 'bcrypt_password@wonderland.lit', 'password' => BCrypt::Password.create('secret')})
         | 
| 15 | 
            +
                  db.collection(:users).save({
         | 
| 16 | 
            +
                    '_id'      => 'full@wonderland.lit',
         | 
| 17 | 
            +
                    'password' => BCrypt::Password.create('secret'),
         | 
| 18 | 
            +
                    'name'     => 'Tester',
         | 
| 19 | 
            +
                    'roster'   => {
         | 
| 20 | 
            +
                      'contact1@wonderland.lit' => {
         | 
| 21 | 
            +
                        'name'   => 'Contact1',
         | 
| 22 | 
            +
                        'groups' => %w[Group1 Group2]
         | 
| 23 | 
            +
                      },
         | 
| 24 | 
            +
                      'contact2@wonderland.lit' => {
         | 
| 25 | 
            +
                        'name'   => 'Contact2',
         | 
| 26 | 
            +
                        'groups' => %w[Group3 Group4]
         | 
| 27 | 
            +
                      }
         | 
| 28 | 
            +
                    }
         | 
| 29 | 
            +
                  })
         | 
| 30 | 
            +
                  db.collection(:vcards).save({'_id' => 'full@wonderland.lit', 'card' => vcard.to_xml})
         | 
| 31 | 
            +
                  db.collection(:fragments).save({'_id' => "full@wonderland.lit:#{fragment_id}", 'xml' => fragment.to_xml})
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              after do
         | 
| 36 | 
            +
                MOCK_MONGO.clear
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              def storage
         | 
| 40 | 
            +
                storage = Vines::Storage::MongoDB.new do
         | 
| 41 | 
            +
                  host 'localhost', 27017
         | 
| 42 | 
            +
                  database 'xmpp_testcase'
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
                def storage.db
         | 
| 45 | 
            +
                  MOCK_MONGO
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
                storage
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              describe 'creating a new instance' do
         | 
| 51 | 
            +
                it 'raises with no database' do
         | 
| 52 | 
            +
                  fibered do
         | 
| 53 | 
            +
                    -> { Vines::Storage::MongoDB.new {} }.must_raise RuntimeError
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                it 'raises with no host' do
         | 
| 58 | 
            +
                  fibered do
         | 
| 59 | 
            +
                    -> { Vines::Storage::MongoDB.new { database 'test' } }.must_raise RuntimeError
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                it 'raises with duplicate hosts' do
         | 
| 64 | 
            +
                  fibered do
         | 
| 65 | 
            +
                    proc do
         | 
| 66 | 
            +
                      Vines::Storage::MongoDB.new do
         | 
| 67 | 
            +
                        host 'localhost', 27017
         | 
| 68 | 
            +
                        host 'localhost', 27017
         | 
| 69 | 
            +
                        database 'test'
         | 
| 70 | 
            +
                      end
         | 
| 71 | 
            +
                    end.must_raise RuntimeError
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                it 'does not raise with host and database' do
         | 
| 76 | 
            +
                  fibered do
         | 
| 77 | 
            +
                    obj =
         | 
| 78 | 
            +
                      Vines::Storage::MongoDB.new do
         | 
| 79 | 
            +
                        host 'localhost', 27017
         | 
| 80 | 
            +
                        database 'test'
         | 
| 81 | 
            +
                      end
         | 
| 82 | 
            +
                    obj.wont_be_nil
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                it 'does not raise with two hosts' do
         | 
| 87 | 
            +
                  fibered do
         | 
| 88 | 
            +
                    obj =
         | 
| 89 | 
            +
                      Vines::Storage::MongoDB.new do
         | 
| 90 | 
            +
                        host 'localhost', 27017
         | 
| 91 | 
            +
                        host 'localhost', 27018
         | 
| 92 | 
            +
                        database 'test'
         | 
| 93 | 
            +
                      end
         | 
| 94 | 
            +
                    obj.wont_be_nil
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
            end
         | 
| @@ -0,0 +1,182 @@ | |
| 1 | 
            +
            # Mixin methods for storage implementation test classes. The behavioral
         | 
| 2 | 
            +
            # tests are the same regardless of implementation so share those methods
         | 
| 3 | 
            +
            # here.
         | 
| 4 | 
            +
            module StorageSpecs
         | 
| 5 | 
            +
              def fragment_id
         | 
| 6 | 
            +
                Digest::SHA1.hexdigest("characters:urn:wonderland")
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def fragment
         | 
| 10 | 
            +
                Nokogiri::XML(%q{
         | 
| 11 | 
            +
                  <characters xmlns="urn:wonderland">
         | 
| 12 | 
            +
                    <character>Alice</character>
         | 
| 13 | 
            +
                  </characters>
         | 
| 14 | 
            +
                }.strip).root
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              def vcard
         | 
| 18 | 
            +
                Nokogiri::XML(%q{
         | 
| 19 | 
            +
                  <vCard xmlns="vcard-temp">
         | 
| 20 | 
            +
                    <FN>Alice in Wonderland</FN>
         | 
| 21 | 
            +
                  </vCard>
         | 
| 22 | 
            +
                }.strip).root
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              def fibered
         | 
| 26 | 
            +
                EM.run do
         | 
| 27 | 
            +
                  Fiber.new do
         | 
| 28 | 
            +
                    yield
         | 
| 29 | 
            +
                    EM.stop
         | 
| 30 | 
            +
                  end.resume
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              def test_authenticate
         | 
| 35 | 
            +
                fibered do
         | 
| 36 | 
            +
                  db = storage
         | 
| 37 | 
            +
                  db.authenticate(nil, nil).must_be_nil
         | 
| 38 | 
            +
                  db.authenticate(nil, 'secret').must_be_nil
         | 
| 39 | 
            +
                  db.authenticate('bogus', nil).must_be_nil
         | 
| 40 | 
            +
                  db.authenticate('bogus', 'secret').must_be_nil
         | 
| 41 | 
            +
                  db.authenticate('empty@wonderland.lit', 'secret').must_be_nil
         | 
| 42 | 
            +
                  db.authenticate('no_password@wonderland.lit', 'secret').must_be_nil
         | 
| 43 | 
            +
                  db.authenticate('clear_password@wonderland.lit', 'secret').must_be_nil
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  user = db.authenticate('bcrypt_password@wonderland.lit', 'secret')
         | 
| 46 | 
            +
                  user.wont_be_nil
         | 
| 47 | 
            +
                  user.jid.to_s.must_equal 'bcrypt_password@wonderland.lit'
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  user = db.authenticate('full@wonderland.lit', 'secret')
         | 
| 50 | 
            +
                  user.wont_be_nil
         | 
| 51 | 
            +
                  user.name.must_equal 'Tester'
         | 
| 52 | 
            +
                  user.jid.to_s.must_equal 'full@wonderland.lit'
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  user.roster.length.must_equal 2
         | 
| 55 | 
            +
                  user.roster[0].jid.to_s.must_equal 'contact1@wonderland.lit'
         | 
| 56 | 
            +
                  user.roster[0].name.must_equal 'Contact1'
         | 
| 57 | 
            +
                  user.roster[0].groups.length.must_equal 2
         | 
| 58 | 
            +
                  user.roster[0].groups[0].must_equal 'Group1'
         | 
| 59 | 
            +
                  user.roster[0].groups[1].must_equal 'Group2'
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  user.roster[1].jid.to_s.must_equal 'contact2@wonderland.lit'
         | 
| 62 | 
            +
                  user.roster[1].name.must_equal 'Contact2'
         | 
| 63 | 
            +
                  user.roster[1].groups.length.must_equal 2
         | 
| 64 | 
            +
                  user.roster[1].groups[0].must_equal 'Group3'
         | 
| 65 | 
            +
                  user.roster[1].groups[1].must_equal 'Group4'
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              def test_find_user
         | 
| 70 | 
            +
                fibered do
         | 
| 71 | 
            +
                  db = storage
         | 
| 72 | 
            +
                  user = db.find_user(nil)
         | 
| 73 | 
            +
                  user.must_be_nil
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  user = db.find_user('full@wonderland.lit')
         | 
| 76 | 
            +
                  user.wont_be_nil
         | 
| 77 | 
            +
                  user.jid.to_s.must_equal 'full@wonderland.lit'
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  user = db.find_user(Vines::JID.new('full@wonderland.lit'))
         | 
| 80 | 
            +
                  user.wont_be_nil
         | 
| 81 | 
            +
                  user.jid.to_s.must_equal 'full@wonderland.lit'
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  user = db.find_user(Vines::JID.new('full@wonderland.lit/resource'))
         | 
| 84 | 
            +
                  user.wont_be_nil
         | 
| 85 | 
            +
                  user.jid.to_s.must_equal 'full@wonderland.lit'
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              def test_save_user
         | 
| 90 | 
            +
                fibered do
         | 
| 91 | 
            +
                  db = storage
         | 
| 92 | 
            +
                  user = Vines::User.new(
         | 
| 93 | 
            +
                    jid: 'save_user@domain.tld/resource1',
         | 
| 94 | 
            +
                    name: 'Save User',
         | 
| 95 | 
            +
                    password: 'secret')
         | 
| 96 | 
            +
                  user.roster << Vines::Contact.new(
         | 
| 97 | 
            +
                    jid: 'contact1@domain.tld/resource2',
         | 
| 98 | 
            +
                    name: 'Contact 1')
         | 
| 99 | 
            +
                  db.save_user(user)
         | 
| 100 | 
            +
                  user = db.find_user('save_user@domain.tld')
         | 
| 101 | 
            +
                  user.wont_be_nil
         | 
| 102 | 
            +
                  user.jid.to_s.must_equal 'save_user@domain.tld'
         | 
| 103 | 
            +
                  user.name.must_equal 'Save User'
         | 
| 104 | 
            +
                  user.roster.length.must_equal 1
         | 
| 105 | 
            +
                  user.roster[0].jid.to_s.must_equal 'contact1@domain.tld'
         | 
| 106 | 
            +
                  user.roster[0].name.must_equal 'Contact 1'
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
              end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
              def test_find_vcard
         | 
| 111 | 
            +
                fibered do
         | 
| 112 | 
            +
                  db = storage
         | 
| 113 | 
            +
                  card = db.find_vcard(nil)
         | 
| 114 | 
            +
                  card.must_be_nil
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                  card = db.find_vcard('full@wonderland.lit')
         | 
| 117 | 
            +
                  card.wont_be_nil
         | 
| 118 | 
            +
                  card.to_s.must_equal vcard.to_s
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                  card = db.find_vcard(Vines::JID.new('full@wonderland.lit'))
         | 
| 121 | 
            +
                  card.wont_be_nil
         | 
| 122 | 
            +
                  card.to_s.must_equal vcard.to_s
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                  card = db.find_vcard(Vines::JID.new('full@wonderland.lit/resource'))
         | 
| 125 | 
            +
                  card.wont_be_nil
         | 
| 126 | 
            +
                  card.to_s.must_equal vcard.to_s
         | 
| 127 | 
            +
                end
         | 
| 128 | 
            +
              end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
              def test_save_vcard
         | 
| 131 | 
            +
                fibered do
         | 
| 132 | 
            +
                  db = storage
         | 
| 133 | 
            +
                  db.save_user(Vines::User.new(jid: 'save_user@domain.tld'))
         | 
| 134 | 
            +
                  db.save_vcard('save_user@domain.tld/resource1', vcard)
         | 
| 135 | 
            +
                  card = db.find_vcard('save_user@domain.tld')
         | 
| 136 | 
            +
                  card.wont_be_nil
         | 
| 137 | 
            +
                  card.to_s.must_equal vcard.to_s
         | 
| 138 | 
            +
                end
         | 
| 139 | 
            +
              end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
              def test_find_fragment
         | 
| 142 | 
            +
                fibered do
         | 
| 143 | 
            +
                  db = storage
         | 
| 144 | 
            +
                  root = Nokogiri::XML(%q{<characters xmlns="urn:wonderland"/>}).root
         | 
| 145 | 
            +
                  bad_name = Nokogiri::XML(%q{<not_characters xmlns="urn:wonderland"/>}).root
         | 
| 146 | 
            +
                  bad_ns = Nokogiri::XML(%q{<characters xmlns="not:wonderland"/>}).root
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  node = db.find_fragment(nil, nil)
         | 
| 149 | 
            +
                  node.must_be_nil
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                  node = db.find_fragment('full@wonderland.lit', bad_name)
         | 
| 152 | 
            +
                  node.must_be_nil
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                  node = db.find_fragment('full@wonderland.lit', bad_ns)
         | 
| 155 | 
            +
                  node.must_be_nil
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                  node = db.find_fragment('full@wonderland.lit', root)
         | 
| 158 | 
            +
                  node.wont_be_nil
         | 
| 159 | 
            +
                  node.to_s.must_equal fragment.to_s
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                  node = db.find_fragment(Vines::JID.new('full@wonderland.lit'), root)
         | 
| 162 | 
            +
                  node.wont_be_nil
         | 
| 163 | 
            +
                  node.to_s.must_equal fragment.to_s
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                  node = db.find_fragment(Vines::JID.new('full@wonderland.lit/resource'), root)
         | 
| 166 | 
            +
                  node.wont_be_nil
         | 
| 167 | 
            +
                  node.to_s.must_equal fragment.to_s
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
              end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
              def test_save_fragment
         | 
| 172 | 
            +
                fibered do
         | 
| 173 | 
            +
                  db = storage
         | 
| 174 | 
            +
                  root = Nokogiri::XML(%q{<characters xmlns="urn:wonderland"/>}).root
         | 
| 175 | 
            +
                  db.save_user(Vines::User.new(jid: 'save_user@domain.tld'))
         | 
| 176 | 
            +
                  db.save_fragment('save_user@domain.tld/resource1', fragment)
         | 
| 177 | 
            +
                  node = db.find_fragment('save_user@domain.tld', root)
         | 
| 178 | 
            +
                  node.wont_be_nil
         | 
| 179 | 
            +
                  node.to_s.must_equal fragment.to_s
         | 
| 180 | 
            +
                end
         | 
| 181 | 
            +
              end
         | 
| 182 | 
            +
            end
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            Gem::Specification.new do |s|
         | 
| 2 | 
            +
              s.name         = 'vines-mongodb'
         | 
| 3 | 
            +
              s.version      = '0.1.0'
         | 
| 4 | 
            +
              s.summary      = %q[Provides a MongoDB storage adapter for the Vines XMPP chat server.]
         | 
| 5 | 
            +
              s.description  = %q[Stores Vines user data in MongoDB.]
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              s.authors      = ['David Graham']
         | 
| 8 | 
            +
              s.email        = %w[david@negativecode.com]
         | 
| 9 | 
            +
              s.homepage     = 'http://www.getvines.org'
         | 
| 10 | 
            +
              s.license      = 'MIT'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              s.files        = Dir['[A-Z]*', 'vines-mongodb.gemspec', 'lib/**/*'] - ['Gemfile.lock']
         | 
| 13 | 
            +
              s.test_files   = Dir['spec/**/*']
         | 
| 14 | 
            +
              s.require_path = 'lib'
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              s.add_dependency 'mongo', '~> 1.5.2'
         | 
| 17 | 
            +
              s.add_dependency 'bson_ext', '~> 1.5.2'
         | 
| 18 | 
            +
              s.add_dependency 'vines', '>= 0.4.5'
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              s.add_development_dependency 'minitest', '~> 4.7.4'
         | 
| 21 | 
            +
              s.add_development_dependency 'rake', '~> 10.1.0'
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              s.required_ruby_version = '>= 1.9.3'
         | 
| 24 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,143 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: vines-mongodb
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.1.0
         | 
| 5 | 
            +
              prerelease: 
         | 
| 6 | 
            +
            platform: ruby
         | 
| 7 | 
            +
            authors:
         | 
| 8 | 
            +
            - David Graham
         | 
| 9 | 
            +
            autorequire: 
         | 
| 10 | 
            +
            bindir: bin
         | 
| 11 | 
            +
            cert_chain: []
         | 
| 12 | 
            +
            date: 2013-06-21 00:00:00.000000000 Z
         | 
| 13 | 
            +
            dependencies:
         | 
| 14 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 15 | 
            +
              name: mongo
         | 
| 16 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 17 | 
            +
                none: false
         | 
| 18 | 
            +
                requirements:
         | 
| 19 | 
            +
                - - ~>
         | 
| 20 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 21 | 
            +
                    version: 1.5.2
         | 
| 22 | 
            +
              type: :runtime
         | 
| 23 | 
            +
              prerelease: false
         | 
| 24 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 25 | 
            +
                none: false
         | 
| 26 | 
            +
                requirements:
         | 
| 27 | 
            +
                - - ~>
         | 
| 28 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 29 | 
            +
                    version: 1.5.2
         | 
| 30 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 31 | 
            +
              name: bson_ext
         | 
| 32 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 33 | 
            +
                none: false
         | 
| 34 | 
            +
                requirements:
         | 
| 35 | 
            +
                - - ~>
         | 
| 36 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 37 | 
            +
                    version: 1.5.2
         | 
| 38 | 
            +
              type: :runtime
         | 
| 39 | 
            +
              prerelease: false
         | 
| 40 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 41 | 
            +
                none: false
         | 
| 42 | 
            +
                requirements:
         | 
| 43 | 
            +
                - - ~>
         | 
| 44 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 45 | 
            +
                    version: 1.5.2
         | 
| 46 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 47 | 
            +
              name: vines
         | 
| 48 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 49 | 
            +
                none: false
         | 
| 50 | 
            +
                requirements:
         | 
| 51 | 
            +
                - - ! '>='
         | 
| 52 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 53 | 
            +
                    version: 0.4.5
         | 
| 54 | 
            +
              type: :runtime
         | 
| 55 | 
            +
              prerelease: false
         | 
| 56 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 57 | 
            +
                none: false
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - ! '>='
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: 0.4.5
         | 
| 62 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 63 | 
            +
              name: minitest
         | 
| 64 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                none: false
         | 
| 66 | 
            +
                requirements:
         | 
| 67 | 
            +
                - - ~>
         | 
| 68 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 69 | 
            +
                    version: 4.7.4
         | 
| 70 | 
            +
              type: :development
         | 
| 71 | 
            +
              prerelease: false
         | 
| 72 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 73 | 
            +
                none: false
         | 
| 74 | 
            +
                requirements:
         | 
| 75 | 
            +
                - - ~>
         | 
| 76 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 77 | 
            +
                    version: 4.7.4
         | 
| 78 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 79 | 
            +
              name: rake
         | 
| 80 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 81 | 
            +
                none: false
         | 
| 82 | 
            +
                requirements:
         | 
| 83 | 
            +
                - - ~>
         | 
| 84 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 85 | 
            +
                    version: 10.1.0
         | 
| 86 | 
            +
              type: :development
         | 
| 87 | 
            +
              prerelease: false
         | 
| 88 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 89 | 
            +
                none: false
         | 
| 90 | 
            +
                requirements:
         | 
| 91 | 
            +
                - - ~>
         | 
| 92 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 93 | 
            +
                    version: 10.1.0
         | 
| 94 | 
            +
            description: Stores Vines user data in MongoDB.
         | 
| 95 | 
            +
            email:
         | 
| 96 | 
            +
            - david@negativecode.com
         | 
| 97 | 
            +
            executables: []
         | 
| 98 | 
            +
            extensions: []
         | 
| 99 | 
            +
            extra_rdoc_files: []
         | 
| 100 | 
            +
            files:
         | 
| 101 | 
            +
            - Gemfile
         | 
| 102 | 
            +
            - LICENSE
         | 
| 103 | 
            +
            - Rakefile
         | 
| 104 | 
            +
            - README.md
         | 
| 105 | 
            +
            - vines-mongodb.gemspec
         | 
| 106 | 
            +
            - lib/vines/storage/mongodb.rb
         | 
| 107 | 
            +
            - spec/mock_mongo.rb
         | 
| 108 | 
            +
            - spec/spec_helper.rb
         | 
| 109 | 
            +
            - spec/storage/mongodb_spec.rb
         | 
| 110 | 
            +
            - spec/storage_specs.rb
         | 
| 111 | 
            +
            homepage: http://www.getvines.org
         | 
| 112 | 
            +
            licenses:
         | 
| 113 | 
            +
            - MIT
         | 
| 114 | 
            +
            post_install_message: 
         | 
| 115 | 
            +
            rdoc_options: []
         | 
| 116 | 
            +
            require_paths:
         | 
| 117 | 
            +
            - lib
         | 
| 118 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 119 | 
            +
              none: false
         | 
| 120 | 
            +
              requirements:
         | 
| 121 | 
            +
              - - ! '>='
         | 
| 122 | 
            +
                - !ruby/object:Gem::Version
         | 
| 123 | 
            +
                  version: 1.9.3
         | 
| 124 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 125 | 
            +
              none: false
         | 
| 126 | 
            +
              requirements:
         | 
| 127 | 
            +
              - - ! '>='
         | 
| 128 | 
            +
                - !ruby/object:Gem::Version
         | 
| 129 | 
            +
                  version: '0'
         | 
| 130 | 
            +
                  segments:
         | 
| 131 | 
            +
                  - 0
         | 
| 132 | 
            +
                  hash: 1526150033927747490
         | 
| 133 | 
            +
            requirements: []
         | 
| 134 | 
            +
            rubyforge_project: 
         | 
| 135 | 
            +
            rubygems_version: 1.8.23
         | 
| 136 | 
            +
            signing_key: 
         | 
| 137 | 
            +
            specification_version: 3
         | 
| 138 | 
            +
            summary: Provides a MongoDB storage adapter for the Vines XMPP chat server.
         | 
| 139 | 
            +
            test_files:
         | 
| 140 | 
            +
            - spec/mock_mongo.rb
         | 
| 141 | 
            +
            - spec/spec_helper.rb
         | 
| 142 | 
            +
            - spec/storage/mongodb_spec.rb
         | 
| 143 | 
            +
            - spec/storage_specs.rb
         |