guacamole 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +18 -0
- data/CONTRIBUTING.md +26 -0
- data/Gemfile +16 -0
- data/Gemfile.devtools +55 -0
- data/Guardfile +13 -0
- data/LICENSE +202 -0
- data/README.md +130 -0
- data/Rakefile +7 -0
- data/config/devtools.yml +4 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +3 -0
- data/config/reek.yml +104 -0
- data/config/rubocop.yml +68 -0
- data/config/yardstick.yml +2 -0
- data/guacamole.gemspec +28 -0
- data/lib/guacamole.rb +19 -0
- data/lib/guacamole/collection.rb +246 -0
- data/lib/guacamole/configuration.rb +123 -0
- data/lib/guacamole/document_model_mapper.rb +95 -0
- data/lib/guacamole/model.rb +210 -0
- data/lib/guacamole/query.rb +72 -0
- data/lib/guacamole/railtie.rb +27 -0
- data/lib/guacamole/railtie/database.rake +5 -0
- data/lib/guacamole/version.rb +5 -0
- data/log/.gitkeep +0 -0
- data/spec/acceptance/.gitkeep +0 -0
- data/spec/acceptance/basic_spec.rb +112 -0
- data/spec/acceptance/config/guacamole.yml +11 -0
- data/spec/acceptance/spec_helper.rb +61 -0
- data/spec/fabricators/article_fabricator.rb +10 -0
- data/spec/fabricators/comment_fabricator.rb +5 -0
- data/spec/setup/arangodb.sh +65 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/unit/.gitkeep +0 -0
- data/spec/unit/collection_spec.rb +380 -0
- data/spec/unit/configuration_spec.rb +119 -0
- data/spec/unit/document_model_mapper_spec.rb +120 -0
- data/spec/unit/example_spec.rb +8 -0
- data/spec/unit/model_spec.rb +116 -0
- data/spec/unit/query_spec.rb +140 -0
- data/tasks/adjustments.rake +35 -0
- metadata +191 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'guacamole'
|
4
|
+
require 'guacamole/configuration'
|
5
|
+
|
6
|
+
require 'rails'
|
7
|
+
|
8
|
+
module Guacamole
|
9
|
+
# Class to hook into Rails configuration and initializer
|
10
|
+
# @api private
|
11
|
+
class Railtie < Rails::Railtie
|
12
|
+
|
13
|
+
rake_tasks do
|
14
|
+
load 'guacamole/railtie/database.rake'
|
15
|
+
end
|
16
|
+
|
17
|
+
config.guacamole = ::Guacamole::Configuration
|
18
|
+
|
19
|
+
initializer 'guacamole.load-config' do
|
20
|
+
config_file = Rails.root.join('config', 'guacamole.yml')
|
21
|
+
if config_file.file?
|
22
|
+
Guacamole::Configuration.load config_file
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
data/log/.gitkeep
ADDED
File without changes
|
File without changes
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'guacamole'
|
3
|
+
require 'acceptance/spec_helper'
|
4
|
+
|
5
|
+
class Comment
|
6
|
+
include Guacamole::Model
|
7
|
+
|
8
|
+
attribute :text, String
|
9
|
+
end
|
10
|
+
|
11
|
+
class Article
|
12
|
+
include Guacamole::Model
|
13
|
+
|
14
|
+
attribute :title, String
|
15
|
+
attribute :comments, Array[Comment]
|
16
|
+
|
17
|
+
validates :title, presence: true
|
18
|
+
end
|
19
|
+
|
20
|
+
class ArticlesCollection
|
21
|
+
include Guacamole::Collection
|
22
|
+
|
23
|
+
map do
|
24
|
+
embeds :comments
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'ModelBasics' do
|
29
|
+
|
30
|
+
describe Article do
|
31
|
+
it 'should allow setting its title' do
|
32
|
+
subject.title = 'This is my fancy article'
|
33
|
+
|
34
|
+
expect(subject.title).to eq('This is my fancy article')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should have key and rev attributes' do
|
38
|
+
expect(subject.key).to be_nil
|
39
|
+
expect(subject.rev).to be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should have timestamp attributes which are empty' do
|
43
|
+
expect(subject.created_at).to be_nil
|
44
|
+
expect(subject.updated_at).to be_nil
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should validate its attributes' do
|
48
|
+
expect(subject.valid?).to be_false
|
49
|
+
subject.title = 'The Legend of Zelda'
|
50
|
+
expect(subject.valid?).to be_true
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should know its model name' do
|
54
|
+
# This test passes when you only require ActiveModel::Validations
|
55
|
+
expect(subject.class.model_name).to eq 'Article'
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should convert itself to params' do
|
59
|
+
subject.key = 'random_number'
|
60
|
+
expect(subject.to_param).to eq 'random_number'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'CollectionBasics' do
|
67
|
+
|
68
|
+
describe ArticlesCollection do
|
69
|
+
subject { ArticlesCollection }
|
70
|
+
|
71
|
+
let(:some_article) { Fabricate(:article) }
|
72
|
+
|
73
|
+
it 'should provide a method to find documents by key and return the appropriate model' do
|
74
|
+
found_model = subject.by_key some_article.key
|
75
|
+
expect(found_model).to eq some_article
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should save models to the database' do
|
79
|
+
new_article = Fabricate.build(:article)
|
80
|
+
subject.save new_article
|
81
|
+
|
82
|
+
expect(subject.by_key(new_article.key)).to eq new_article
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should update models in the database' do
|
86
|
+
some_article.title = 'Has been updated'
|
87
|
+
subject.replace some_article
|
88
|
+
|
89
|
+
updated_article = subject.by_key(some_article.key)
|
90
|
+
|
91
|
+
expect(updated_article.title).to eq 'Has been updated'
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should receive all documents by title' do
|
95
|
+
subject.save Fabricate.build(:article, title: 'Disturbed')
|
96
|
+
subject.save Fabricate.build(:article, title: 'Not so Disturbed')
|
97
|
+
|
98
|
+
result = subject.by_example(title: 'Disturbed').first
|
99
|
+
|
100
|
+
expect(result.title).to eq 'Disturbed'
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should allow to nest models' do
|
104
|
+
article_with_comments = Fabricate(:article_with_two_comments)
|
105
|
+
found_article = subject.by_key(article_with_comments.key)
|
106
|
+
|
107
|
+
expect(found_article.comments.first).to be_a Comment
|
108
|
+
expect(found_article.comments).to eq article_with_comments.comments
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# FIXME: This is copied from Ashikawa::Core for now but is not recommended. This
|
2
|
+
# setup uses the default database instead of a custom DB. Due to this we're deleting
|
3
|
+
# all collections in the default database each time we run the specs.
|
4
|
+
# => This is not good!
|
5
|
+
test:
|
6
|
+
protocol: 'http'
|
7
|
+
host: 'localhost'
|
8
|
+
port: 8529
|
9
|
+
password: ''
|
10
|
+
username: ''
|
11
|
+
database: '_system'
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
|
+
|
6
|
+
require 'fabrication'
|
7
|
+
require 'logging'
|
8
|
+
require 'ashikawa-core'
|
9
|
+
|
10
|
+
begin
|
11
|
+
require 'debugger'
|
12
|
+
rescue LoadError
|
13
|
+
puts "Debugger is not available. Maybe you're Travis."
|
14
|
+
end
|
15
|
+
|
16
|
+
class Fabrication::Generator::Guacamole < Fabrication::Generator::Base
|
17
|
+
|
18
|
+
def self.supports?(klass)
|
19
|
+
defined?(Guacamole) && klass.ancestors.include?(Guacamole::Model)
|
20
|
+
end
|
21
|
+
|
22
|
+
def persist
|
23
|
+
collection = [_instance.class.name.pluralize, 'Collection'].join.constantize
|
24
|
+
collection.save _instance
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_instance
|
28
|
+
_instance.valid?
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
Fabrication::Schematic::Definition::GENERATORS.unshift Fabrication::Generator::Guacamole
|
34
|
+
|
35
|
+
ENV['GUACAMOLE_ENV'] = 'test'
|
36
|
+
|
37
|
+
Guacamole.configure do |config|
|
38
|
+
logger = Logging.logger['guacamole_logger']
|
39
|
+
logger.add_appenders(
|
40
|
+
Logging.appenders.file('log/acceptance.log')
|
41
|
+
)
|
42
|
+
logger.level = :info
|
43
|
+
|
44
|
+
config.logger = logger
|
45
|
+
|
46
|
+
config.load File.join(File.dirname(__FILE__), 'config', 'guacamole.yml')
|
47
|
+
end
|
48
|
+
|
49
|
+
RSpec.configure do |config|
|
50
|
+
config.expect_with :rspec do |c|
|
51
|
+
c.syntax = :expect
|
52
|
+
end
|
53
|
+
|
54
|
+
config.mock_with :rspec do |c|
|
55
|
+
c.syntax = :expect
|
56
|
+
end
|
57
|
+
|
58
|
+
config.before(:each) do
|
59
|
+
Guacamole.configuration.database.collections.each { |collection| collection.truncate! }
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Fabricator(:article) do
|
4
|
+
title 'And then there was silence'
|
5
|
+
end
|
6
|
+
|
7
|
+
Fabricator(:article_with_two_comments, from: Article) do
|
8
|
+
title 'I have two comments'
|
9
|
+
comments(count: 2) { |attrs, i| Fabricate.build(:comment, text: "I'm comment number #{i}") }
|
10
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
4
|
+
cd $DIR
|
5
|
+
|
6
|
+
# VERSION=1.4.devel
|
7
|
+
NAME=ArangoDB-$VERSION
|
8
|
+
|
9
|
+
if [ ! -d "$DIR/$NAME" ]; then
|
10
|
+
# download ArangoDB
|
11
|
+
echo "wget http://www.arangodb.org/travisCI/$NAME.tar.gz"
|
12
|
+
wget http://www.arangodb.org/travisCI/$NAME.tar.gz
|
13
|
+
echo "tar zxf $NAME.tar.gz"
|
14
|
+
tar zvxf $NAME.tar.gz
|
15
|
+
fi
|
16
|
+
|
17
|
+
ARCH=$(arch)
|
18
|
+
PID=$(echo $PPID)
|
19
|
+
TMP_DIR="/tmp/arangodb.$PID"
|
20
|
+
PID_FILE="/tmp/arangodb.$PID.pid"
|
21
|
+
ARANGODB_DIR="$DIR/$NAME"
|
22
|
+
|
23
|
+
ARANGOD="${ARANGODB_DIR}/bin/arangod"
|
24
|
+
if [ "$ARCH" == "x86_64" ]; then
|
25
|
+
ARANGOD="${ARANGOD}_x86_64"
|
26
|
+
fi
|
27
|
+
|
28
|
+
# create database directory
|
29
|
+
mkdir ${TMP_DIR}
|
30
|
+
|
31
|
+
echo "Starting arangodb '${ARANGOD}'"
|
32
|
+
|
33
|
+
${ARANGOD} \
|
34
|
+
--database.directory ${TMP_DIR} \
|
35
|
+
--configuration none \
|
36
|
+
--server.endpoint tcp://127.0.0.1:8529 \
|
37
|
+
--javascript.startup-directory ${ARANGODB_DIR}/js \
|
38
|
+
--server.admin-directory ${ARANGODB_DIR}/html/admin \
|
39
|
+
--javascript.modules-path ${ARANGODB_DIR}/js/server/modules:${ARANGODB_DIR}/js/common/modules:${ARANGODB_DIR}/js/node \
|
40
|
+
--javascript.package-path ${ARANGODB_DIR}/js/npm:${ARANGODB_DIR}/js/common/test-data/modules \
|
41
|
+
--javascript.action-directory ${ARANGODB_DIR}/js/actions \
|
42
|
+
--javascript.app-path ${ARANGODB_DIR}/js/apps \
|
43
|
+
--database.maximal-journal-size 1048576 \
|
44
|
+
--server.disable-admin-interface ${ARANGODB_DISABLE_AUTHENTIFICATION} \
|
45
|
+
--server.disable-authentication true \
|
46
|
+
--javascript.gc-interval 1 &
|
47
|
+
|
48
|
+
sleep 2
|
49
|
+
|
50
|
+
echo "Check for arangod process"
|
51
|
+
process=$(ps auxww | grep "bin/arangod" | grep -v grep)
|
52
|
+
|
53
|
+
if [ "x$process" == "x" ]; then
|
54
|
+
echo "no 'arangod' process found"
|
55
|
+
echo "ARCH = $ARCH"
|
56
|
+
exit 1
|
57
|
+
fi
|
58
|
+
|
59
|
+
echo "Waiting until ArangoDB is ready on port 8529"
|
60
|
+
while [[ -z `curl -s 'http://127.0.0.1:8529/_api/version' ` ]] ; do
|
61
|
+
echo -n "."
|
62
|
+
sleep 2s
|
63
|
+
done
|
64
|
+
|
65
|
+
echo "ArangoDB is up"
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'debugger'
|
7
|
+
rescue LoadError
|
8
|
+
puts "Debugger is not available. Maybe you're Travis."
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.expect_with :rspec do |c|
|
13
|
+
c.syntax = :expect
|
14
|
+
end
|
15
|
+
|
16
|
+
config.mock_with :rspec do |c|
|
17
|
+
c.syntax = :expect
|
18
|
+
end
|
19
|
+
end
|
data/spec/unit/.gitkeep
ADDED
File without changes
|
@@ -0,0 +1,380 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'guacamole/collection'
|
5
|
+
|
6
|
+
class Test
|
7
|
+
end
|
8
|
+
|
9
|
+
class TestCollection
|
10
|
+
include Guacamole::Collection
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Guacamole::Collection do
|
14
|
+
subject { TestCollection }
|
15
|
+
|
16
|
+
describe 'Configuration' do
|
17
|
+
it 'should set the connection to the ArangoDB collection' do
|
18
|
+
mock_collection_connection = double('ConnectionToCollection')
|
19
|
+
subject.connection = mock_collection_connection
|
20
|
+
|
21
|
+
expect(subject.connection).to eq mock_collection_connection
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should set the Mapper instance to map documents to models and vice versa' do
|
25
|
+
mock_mapper = double('Mapper')
|
26
|
+
subject.mapper = mock_mapper
|
27
|
+
|
28
|
+
expect(subject.mapper).to eq mock_mapper
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should set the connection to ArangoDB' do
|
32
|
+
mock_db = double('Ashikawa::Core::Database')
|
33
|
+
subject.database = mock_db
|
34
|
+
|
35
|
+
expect(subject.database).to eq mock_db
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should know the name of the collection in ArangoDB' do
|
39
|
+
expect(subject.collection_name).to eq 'test'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should know the class of the model to manage' do
|
43
|
+
expect(subject.model_class).to eq Test
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'database' do
|
48
|
+
before do
|
49
|
+
subject.database = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should default to Guacamole.configuration.database' do
|
53
|
+
default_database = double('Database')
|
54
|
+
configuration = double('Configuration', database: default_database)
|
55
|
+
allow(Guacamole).to receive(:configuration).and_return(configuration)
|
56
|
+
|
57
|
+
expect(subject.database).to eq default_database
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'connection' do
|
62
|
+
before do
|
63
|
+
subject.connection = nil
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should default to the collection "collection_name" in the database' do
|
67
|
+
database = double('Database')
|
68
|
+
allow(subject).to receive(:database).and_return(database)
|
69
|
+
|
70
|
+
expect(database).to receive(:[]).with(subject.collection_name)
|
71
|
+
|
72
|
+
subject.connection
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'mapper' do
|
77
|
+
before do
|
78
|
+
subject.mapper = nil
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should default to Guacamole.configuration.default_mapper' do
|
82
|
+
default_mapper = double('Mapper')
|
83
|
+
mapper_instance = double('MapperInstance')
|
84
|
+
configuration = double('Configuration', default_mapper: default_mapper)
|
85
|
+
allow(Guacamole).to receive(:configuration).and_return(configuration)
|
86
|
+
allow(default_mapper).to receive(:new).with(subject.model_class).and_return(mapper_instance)
|
87
|
+
|
88
|
+
expect(subject.mapper).to eq mapper_instance
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
let(:connection) { double('Connection') }
|
93
|
+
let(:mapper) { double('Mapper') }
|
94
|
+
|
95
|
+
before do
|
96
|
+
subject.connection = connection
|
97
|
+
subject.mapper = mapper
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'by_key' do
|
101
|
+
it 'should get mapped documents by key from the database' do
|
102
|
+
document = { data: 'foo' }
|
103
|
+
model = double('Model')
|
104
|
+
|
105
|
+
expect(connection).to receive(:fetch).with('some_key').and_return(document)
|
106
|
+
expect(mapper).to receive(:document_to_model).with(document).and_return(model)
|
107
|
+
|
108
|
+
expect(subject.by_key('some_key')).to eq model
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should raise a Ashikawa::Core::DocumentNotFoundException exception for nil' do
|
112
|
+
expect { subject.by_key(nil) }.to raise_error(Ashikawa::Core::DocumentNotFoundException)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'save' do
|
117
|
+
|
118
|
+
before do
|
119
|
+
allow(connection).to receive(:create_document).with(document).and_return(document)
|
120
|
+
allow(mapper).to receive(:model_to_document).with(model).and_return(document)
|
121
|
+
end
|
122
|
+
|
123
|
+
let(:key) { double('Key') }
|
124
|
+
let(:rev) { double('Rev') }
|
125
|
+
let(:document) { double('Document', key: key, revision: rev).as_null_object }
|
126
|
+
let(:model) { double('Model').as_null_object }
|
127
|
+
|
128
|
+
context 'a valid model' do
|
129
|
+
before do
|
130
|
+
allow(model).to receive(:valid?).and_return(true)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should create a document' do
|
134
|
+
expect(connection).to receive(:create_document).with(document).and_return(document)
|
135
|
+
expect(mapper).to receive(:model_to_document).with(model).and_return(document)
|
136
|
+
|
137
|
+
subject.save model
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should return the model after calling save' do
|
141
|
+
expect(subject.save(model)).to eq model
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'should set timestamps before creating the document' do
|
145
|
+
now = double('Time.now')
|
146
|
+
|
147
|
+
allow(Time).to receive(:now).once.and_return(now)
|
148
|
+
|
149
|
+
expect(model).to receive(:created_at=).with(now).ordered
|
150
|
+
expect(model).to receive(:updated_at=).with(now).ordered
|
151
|
+
|
152
|
+
allow(connection).to receive(:create_document).with(document).and_return(document).ordered
|
153
|
+
|
154
|
+
subject.save model
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should add key to model' do
|
158
|
+
expect(model).to receive(:key=).with(key)
|
159
|
+
|
160
|
+
subject.save model
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should add rev to model' do
|
164
|
+
expect(model).to receive(:rev=).with(rev)
|
165
|
+
|
166
|
+
subject.save model
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context 'an invalid model' do
|
171
|
+
|
172
|
+
before do
|
173
|
+
expect(model).to receive(:valid?).and_return(false)
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should not be used to create the document' do
|
177
|
+
expect(connection).to receive(:create_document).never
|
178
|
+
|
179
|
+
subject.save model
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'should not be changed' do
|
183
|
+
expect(model).to receive(:created_at=).never
|
184
|
+
expect(model).to receive(:updated_at=).never
|
185
|
+
expect(model).to receive(:key=).never
|
186
|
+
expect(model).to receive(:rev=).never
|
187
|
+
|
188
|
+
subject.save model
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should return false' do
|
192
|
+
expect(subject.save(model)).to be false
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
describe 'delete' do
|
198
|
+
let(:document) { double('Document') }
|
199
|
+
let(:key) { double('Key') }
|
200
|
+
let(:model) { double('Model', key: key) }
|
201
|
+
|
202
|
+
before do
|
203
|
+
allow(connection).to receive(:fetch).with(key).and_return(document)
|
204
|
+
allow(document).to receive(:delete)
|
205
|
+
end
|
206
|
+
|
207
|
+
context 'a key was provided' do
|
208
|
+
it 'should delete the according document' do
|
209
|
+
expect(document).to receive(:delete)
|
210
|
+
|
211
|
+
subject.delete key
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'should return the according key' do
|
215
|
+
expect(subject.delete(key)).to eq key
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context 'a model was provided' do
|
220
|
+
it 'should delete the according document' do
|
221
|
+
expect(document).to receive(:delete)
|
222
|
+
|
223
|
+
subject.delete model
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'should return the according key' do
|
227
|
+
expect(subject.delete(model)).to eq key
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe 'replace' do
|
233
|
+
let(:key) { double('Key') }
|
234
|
+
let(:rev) { double('Rev') }
|
235
|
+
let(:model) { double('Model', key: key).as_null_object }
|
236
|
+
let(:document) { double('Document').as_null_object }
|
237
|
+
let(:response) { double('Hash') }
|
238
|
+
|
239
|
+
before do
|
240
|
+
allow(mapper).to receive(:model_to_document).with(model).and_return(document)
|
241
|
+
allow(connection).to receive(:replace).and_return(response)
|
242
|
+
allow(response).to receive(:[]).with('_rev').and_return(rev)
|
243
|
+
end
|
244
|
+
|
245
|
+
context 'a valid model' do
|
246
|
+
before do
|
247
|
+
allow(model).to receive(:valid?).and_return(true)
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'should set the updated_at timestamp before replacing the document' do
|
251
|
+
now = double('Time.now')
|
252
|
+
|
253
|
+
allow(Time).to receive(:now).once.and_return(now)
|
254
|
+
expect(model).to receive(:updated_at=).with(now)
|
255
|
+
|
256
|
+
subject.replace model
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'should replace the document by key via the connection' do
|
260
|
+
expect(connection).to receive(:replace).with(key, document)
|
261
|
+
|
262
|
+
subject.replace model
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'should update the revision after replacing the document' do
|
266
|
+
allow(connection).to receive(:replace).and_return(response).ordered
|
267
|
+
expect(model).to receive(:rev=).with(rev).ordered
|
268
|
+
|
269
|
+
subject.replace model
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'should return the model' do
|
273
|
+
expect(subject.replace(model)).to eq model
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'should not update created_at' do
|
277
|
+
expect(model).to receive(:created_at=).never
|
278
|
+
|
279
|
+
subject.replace model
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
context 'an invalid model' do
|
284
|
+
before do
|
285
|
+
allow(model).to receive(:valid?).and_return(false)
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'should not be used to replace the document' do
|
289
|
+
expect(connection).to receive(:replace).never
|
290
|
+
|
291
|
+
subject.replace model
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'should not be changed' do
|
295
|
+
expect(model).to receive(:rev=).never
|
296
|
+
expect(model).to receive(:updated_at=).never
|
297
|
+
|
298
|
+
subject.replace model
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'should return false' do
|
302
|
+
expect(subject.replace(model)).to be false
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
describe 'by_example' do
|
308
|
+
let(:example) { double }
|
309
|
+
let(:query_connection) { double }
|
310
|
+
let(:query) { double }
|
311
|
+
|
312
|
+
before do
|
313
|
+
allow(connection).to receive(:query)
|
314
|
+
.and_return(query_connection)
|
315
|
+
|
316
|
+
allow(Guacamole::Query).to receive(:new)
|
317
|
+
.and_return(query)
|
318
|
+
|
319
|
+
allow(query).to receive(:example=)
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'should create a new query with the query connection and mapper' do
|
323
|
+
expect(Guacamole::Query).to receive(:new)
|
324
|
+
.with(query_connection, mapper)
|
325
|
+
|
326
|
+
subject.by_example(example)
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'should set the example for the query' do
|
330
|
+
expect(query).to receive(:example=)
|
331
|
+
.with(example)
|
332
|
+
|
333
|
+
subject.by_example(example)
|
334
|
+
end
|
335
|
+
|
336
|
+
it 'should return the query' do
|
337
|
+
expect(subject.by_example(example)).to be query
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
describe 'all' do
|
342
|
+
let(:query_connection) { double }
|
343
|
+
let(:query) { double }
|
344
|
+
|
345
|
+
before do
|
346
|
+
allow(connection).to receive(:query)
|
347
|
+
.and_return(query_connection)
|
348
|
+
|
349
|
+
allow(Guacamole::Query).to receive(:new)
|
350
|
+
.and_return(query)
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'should create a new query with the query connection and mapper' do
|
354
|
+
expect(Guacamole::Query).to receive(:new)
|
355
|
+
.with(query_connection, mapper)
|
356
|
+
|
357
|
+
subject.all
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'should return the query' do
|
361
|
+
expect(subject.all).to be query
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
describe 'map' do
|
366
|
+
let(:mapper) { double('Mapper') }
|
367
|
+
|
368
|
+
before do
|
369
|
+
subject.mapper = mapper
|
370
|
+
end
|
371
|
+
|
372
|
+
it 'should evaluate the block on the mapper instance' do
|
373
|
+
expect(mapper).to receive(:method_to_call_on_mapper)
|
374
|
+
|
375
|
+
subject.map do
|
376
|
+
method_to_call_on_mapper
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|