guacamole 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,119 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'guacamole/configuration'
|
5
|
+
|
6
|
+
describe 'Guacamole.configure' do
|
7
|
+
subject { Guacamole }
|
8
|
+
|
9
|
+
it 'should yield the Configuration class' do
|
10
|
+
subject.configure do |config|
|
11
|
+
expect(config).to eq Guacamole::Configuration
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'Guacamole.configuration' do
|
17
|
+
subject { Guacamole }
|
18
|
+
|
19
|
+
it 'should return the Configuration class' do
|
20
|
+
expect(Guacamole.configuration).to eq Guacamole::Configuration
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe Guacamole::Configuration do
|
25
|
+
subject { Guacamole::Configuration }
|
26
|
+
|
27
|
+
describe 'database' do
|
28
|
+
it 'should set the logger' do
|
29
|
+
database = double('Database')
|
30
|
+
subject.database = database
|
31
|
+
|
32
|
+
expect(subject.database).to eq database
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'default_mapper' do
|
37
|
+
it 'should set the default mapper' do
|
38
|
+
default_mapper = double('Mapper')
|
39
|
+
subject.default_mapper = default_mapper
|
40
|
+
|
41
|
+
expect(subject.default_mapper).to eq default_mapper
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should return Guacamole::DocumentModelMapper as default' do
|
45
|
+
subject.default_mapper = nil
|
46
|
+
|
47
|
+
expect(subject.default_mapper).to eq Guacamole::DocumentModelMapper
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'logger' do
|
52
|
+
before do
|
53
|
+
subject.logger = nil
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should set the logger' do
|
57
|
+
logger = double('Logger')
|
58
|
+
allow(logger).to receive(:level=)
|
59
|
+
subject.logger = logger
|
60
|
+
|
61
|
+
expect(subject.logger).to eq logger
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should default to Logger.new(STDOUT)' do
|
65
|
+
expect(subject.logger).to be_a Logger
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should set the log level to :info for the default logger' do
|
69
|
+
expect(subject.logger.level).to eq Logger::INFO
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'load' do
|
74
|
+
let(:config) { double('Config') }
|
75
|
+
let(:current_environment) { 'development' }
|
76
|
+
|
77
|
+
before do
|
78
|
+
allow(subject).to receive(:current_environment).and_return(current_environment)
|
79
|
+
allow(subject).to receive(:database=)
|
80
|
+
allow(subject).to receive(:create_database_connection_from)
|
81
|
+
allow(config).to receive(:[]).with('development')
|
82
|
+
allow(YAML).to receive(:load_file).with('config_file.yml').and_return(config)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should parse a YAML configuration' do
|
86
|
+
expect(YAML).to receive(:load_file).with('config_file.yml').and_return(config)
|
87
|
+
|
88
|
+
subject.load 'config_file.yml'
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should load the part for the current environment from the config file' do
|
92
|
+
expect(config).to receive(:[]).with(current_environment)
|
93
|
+
|
94
|
+
subject.load 'config_file.yml'
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should create an Ashikawa::Core::Database instance based on configuration' do
|
98
|
+
arango_config = double('ArangoConfig')
|
99
|
+
expect(arango_config).to receive(:url=).with('http://localhost:8529/_db/test_db')
|
100
|
+
expect(arango_config).to receive(:username=).with('')
|
101
|
+
expect(arango_config).to receive(:password=).with('')
|
102
|
+
expect(arango_config).to receive(:logger=).with(subject.logger)
|
103
|
+
|
104
|
+
allow(Ashikawa::Core::Database).to receive(:new).and_yield(arango_config)
|
105
|
+
|
106
|
+
allow(config).to receive(:[]).with('development').and_return(
|
107
|
+
'protocol' => 'http',
|
108
|
+
'host' => 'localhost',
|
109
|
+
'port' => 8529,
|
110
|
+
'username' => '',
|
111
|
+
'password' => '',
|
112
|
+
'database' => 'test_db'
|
113
|
+
)
|
114
|
+
allow(subject).to receive(:create_database_connection_from).and_call_original
|
115
|
+
|
116
|
+
subject.load 'config_file.yml'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'guacamole/document_model_mapper'
|
5
|
+
|
6
|
+
class FancyModel
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Guacamole::DocumentModelMapper do
|
10
|
+
subject { Guacamole::DocumentModelMapper }
|
11
|
+
|
12
|
+
it 'should be initialized with a model class' do
|
13
|
+
mapper = subject.new FancyModel
|
14
|
+
expect(mapper.model_class).to eq FancyModel
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'document_to_model' do
|
18
|
+
subject { Guacamole::DocumentModelMapper.new FancyModel }
|
19
|
+
|
20
|
+
let(:document) { double('Ashikawa::Core::Document') }
|
21
|
+
let(:document_attributes) { double('Hash') }
|
22
|
+
let(:model_instance) { double('ModelInstance').as_null_object }
|
23
|
+
let(:some_key) { double('Key') }
|
24
|
+
let(:some_rev) { double('Rev') }
|
25
|
+
|
26
|
+
before do
|
27
|
+
allow(subject.model_class).to receive(:new).and_return(model_instance)
|
28
|
+
allow(document).to receive(:hash).and_return(document_attributes)
|
29
|
+
allow(document).to receive(:key).and_return(some_key)
|
30
|
+
allow(document).to receive(:revision).and_return(some_rev)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should create a new model instance from an Ashikawa::Core::Document' do
|
34
|
+
expect(subject.model_class).to receive(:new).with(document_attributes)
|
35
|
+
|
36
|
+
model = subject.document_to_model document
|
37
|
+
expect(model).to eq model_instance
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should set the rev and key on a new model instance' do
|
41
|
+
expect(model_instance).to receive(:key=).with(some_key)
|
42
|
+
expect(model_instance).to receive(:rev=).with(some_rev)
|
43
|
+
|
44
|
+
subject.document_to_model document
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with embedded ponies' do
|
48
|
+
# This is handled by Virtus, we just need to provide a hash
|
49
|
+
# and the coercing will be taken care of by Virtus
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'model_to_document' do
|
54
|
+
subject { Guacamole::DocumentModelMapper.new FancyModel }
|
55
|
+
|
56
|
+
let(:model) { double('Model') }
|
57
|
+
let(:model_attributes) { double('Hash').as_null_object }
|
58
|
+
|
59
|
+
before do
|
60
|
+
allow(model).to receive(:attributes).and_return(model_attributes)
|
61
|
+
allow(model_attributes).to receive(:dup).and_return(model_attributes)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should transform a model into a simple hash' do
|
65
|
+
expect(subject.model_to_document(model)).to eq model_attributes
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should return a copy of the model attributes hash' do
|
69
|
+
expect(model_attributes).to receive(:dup).and_return(model_attributes)
|
70
|
+
|
71
|
+
subject.model_to_document(model)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should remove the key and rev attributes from the document' do
|
75
|
+
expect(model_attributes).to receive(:except).with(:key, :rev)
|
76
|
+
|
77
|
+
subject.model_to_document(model)
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'with embedded ponies' do
|
81
|
+
let(:somepony) { double('Pony') }
|
82
|
+
let(:pony_array) { [somepony] }
|
83
|
+
let(:ponylicious_attributes) { double('Hash').as_null_object }
|
84
|
+
|
85
|
+
before do
|
86
|
+
subject.embeds :ponies
|
87
|
+
|
88
|
+
allow(model).to receive(:ponies)
|
89
|
+
.and_return pony_array
|
90
|
+
|
91
|
+
allow(somepony).to receive(:attributes)
|
92
|
+
.and_return ponylicious_attributes
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should convert all embedded ponies to pony hashes' do
|
96
|
+
expect(somepony).to receive(:attributes)
|
97
|
+
.and_return ponylicious_attributes
|
98
|
+
|
99
|
+
subject.model_to_document(model)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should exclude key and rev on embedded ponies' do
|
103
|
+
expect(ponylicious_attributes).to receive(:except)
|
104
|
+
.with(:key, :rev)
|
105
|
+
|
106
|
+
subject.model_to_document(model)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe 'embed' do
|
112
|
+
subject { Guacamole::DocumentModelMapper.new FancyModel }
|
113
|
+
|
114
|
+
it 'should remember which models to embed' do
|
115
|
+
subject.embeds :ponies
|
116
|
+
|
117
|
+
expect(subject.models_to_embed).to include :ponies
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'guacamole/model'
|
5
|
+
|
6
|
+
class TestModel
|
7
|
+
include Guacamole::Model
|
8
|
+
end
|
9
|
+
|
10
|
+
class OtherModel
|
11
|
+
include Guacamole::Model
|
12
|
+
end
|
13
|
+
|
14
|
+
describe Guacamole::Model do
|
15
|
+
subject { TestModel }
|
16
|
+
let(:current_time) { Time.now }
|
17
|
+
|
18
|
+
describe 'module inclusion' do
|
19
|
+
it 'should include Virtus.model' do
|
20
|
+
expect(subject.ancestors.any? do |ancestor|
|
21
|
+
ancestor.to_s.include? 'Virtus'
|
22
|
+
end).to be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should include ActiveModel::Validation' do
|
26
|
+
expect(subject.ancestors).to include ActiveModel::Validations
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should include ActiveModel::Naming' do
|
30
|
+
expect(subject.ancestors).to include ActiveModel::Naming
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should include ActiveModel::Conversion' do
|
34
|
+
expect(subject.ancestors).to include ActiveModel::Conversion
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'default attributes' do
|
39
|
+
subject { TestModel.new }
|
40
|
+
|
41
|
+
it 'should add the key attribute' do
|
42
|
+
subject.key = '12345'
|
43
|
+
expect(subject.key).to eq '12345'
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should add the rev attribute' do
|
47
|
+
subject.rev = '98765'
|
48
|
+
expect(subject.rev).to eq '98765'
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should add the created_at attribute' do
|
52
|
+
subject.created_at = current_time
|
53
|
+
expect(subject.created_at).to be current_time
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should add the updated_at attribute' do
|
57
|
+
subject.updated_at = current_time
|
58
|
+
expect(subject.updated_at).to be current_time
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'persisted?' do
|
63
|
+
subject { TestModel.new }
|
64
|
+
|
65
|
+
it 'should be persisted if it has a key' do
|
66
|
+
subject.key = 'my_key'
|
67
|
+
expect(subject.persisted?).to be_true
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should not be persisted if it doesn't have a key" do
|
71
|
+
subject.key = nil
|
72
|
+
expect(subject.persisted?).to be_false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'id' do
|
77
|
+
subject { TestModel.new }
|
78
|
+
|
79
|
+
it 'should alias key to id for ActiveModel::Conversion compliance' do
|
80
|
+
subject.key = 'my_key'
|
81
|
+
expect(subject.id).to eq 'my_key'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '==' do
|
86
|
+
let(:key) { double('Key') }
|
87
|
+
let(:rev) { double('Rev') }
|
88
|
+
let(:updated_at) { Time.now }
|
89
|
+
let(:content) { double('String') }
|
90
|
+
let(:unixy_time) { 1_445_444_940 } # If you read this line and understand it, you get a beer
|
91
|
+
let(:timestamp_without_nsecs) { Time.at(unixy_time, 0) }
|
92
|
+
let(:timestamp_with_nsecs) { Time.at(unixy_time, 42) }
|
93
|
+
|
94
|
+
subject { TestModel.new(key: key, rev: rev, updated_at: updated_at, content: content) }
|
95
|
+
let(:comparison_object) { TestModel.new(subject.attributes) }
|
96
|
+
|
97
|
+
it 'should not be equal if it is a different class' do
|
98
|
+
expect(subject).to_not eq double
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should be equal if all attributes are equal' do
|
102
|
+
expect(subject).to eq comparison_object
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should be equal if the time is equal in string representation' do
|
106
|
+
subject.updated_at = timestamp_with_nsecs
|
107
|
+
comparison_object.updated_at = timestamp_without_nsecs
|
108
|
+
|
109
|
+
expect(subject).to eq comparison_object
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should alias `eql?` to `==`' do
|
113
|
+
expect(subject).to eql comparison_object
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'guacamole/query'
|
5
|
+
|
6
|
+
describe Guacamole::Query do
|
7
|
+
let(:connection) { double('Connection') }
|
8
|
+
let(:mapper) { double('Mapper') }
|
9
|
+
|
10
|
+
subject { Guacamole::Query.new(connection, mapper) }
|
11
|
+
|
12
|
+
its(:connection) { should be connection }
|
13
|
+
its(:mapper) { should be mapper }
|
14
|
+
|
15
|
+
it 'should be enumerable' do
|
16
|
+
expect(Guacamole::Query.ancestors).to include Enumerable
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'each' do
|
20
|
+
let(:result) { double }
|
21
|
+
let(:document) { double }
|
22
|
+
let(:model) { double }
|
23
|
+
let(:limit) { double }
|
24
|
+
let(:skip) { double }
|
25
|
+
|
26
|
+
before do
|
27
|
+
allow(result).to receive(:each)
|
28
|
+
.and_yield(document)
|
29
|
+
|
30
|
+
allow(mapper).to receive(:document_to_model)
|
31
|
+
.and_return(model)
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'no example was provided' do
|
35
|
+
before do
|
36
|
+
allow(connection).to receive(:all)
|
37
|
+
.and_return(result)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should get all documents' do
|
41
|
+
expect(connection).to receive(:all)
|
42
|
+
.with({})
|
43
|
+
|
44
|
+
subject.each { }
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should iterate over the resulting documents' do
|
48
|
+
expect(result).to receive(:each)
|
49
|
+
|
50
|
+
subject.each { }
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should yield the models to the caller' do
|
54
|
+
expect { |b| subject.each(&b) }.to yield_with_args(model)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should return an enumerator when called without a block' do
|
58
|
+
expect(subject.each).to be_an Enumerator
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should accept a limit' do
|
62
|
+
expect(connection).to receive(:all)
|
63
|
+
.with(hash_including limit: limit)
|
64
|
+
|
65
|
+
subject.limit(limit).each { }
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should accept a skip' do
|
69
|
+
expect(connection).to receive(:all)
|
70
|
+
.with(hash_including skip: skip)
|
71
|
+
|
72
|
+
subject.skip(skip).each { }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'an example was provided' do
|
77
|
+
let(:example) { double }
|
78
|
+
before do
|
79
|
+
subject.example = example
|
80
|
+
|
81
|
+
allow(connection).to receive(:by_example)
|
82
|
+
.and_return(result)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should query by the given example' do
|
86
|
+
expect(connection).to receive(:by_example)
|
87
|
+
.with(example, {})
|
88
|
+
|
89
|
+
subject.each { }
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should iterate over the resulting documents' do
|
93
|
+
expect(result).to receive(:each)
|
94
|
+
|
95
|
+
subject.each { }
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should yield the models to the caller' do
|
99
|
+
expect { |b| subject.each(&b) }.to yield_with_args(model)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should return an enumerator when called without a block' do
|
103
|
+
expect(subject.each).to be_an Enumerator
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should accept a limit' do
|
107
|
+
expect(connection).to receive(:by_example)
|
108
|
+
.with(example, hash_including(limit: limit))
|
109
|
+
|
110
|
+
subject.limit(limit).each { }
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should accept a skip' do
|
114
|
+
expect(connection).to receive(:by_example)
|
115
|
+
.with(example, hash_including(skip: skip))
|
116
|
+
|
117
|
+
subject.skip(skip).each { }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'first' do
|
123
|
+
context 'no example was provided' do
|
124
|
+
it 'should return the first result of the all query' do
|
125
|
+
first_result = double
|
126
|
+
first_result_as_model = double
|
127
|
+
results = [first_result]
|
128
|
+
|
129
|
+
allow(mapper).to receive(:document_to_model)
|
130
|
+
.with(first_result)
|
131
|
+
.and_return(first_result_as_model)
|
132
|
+
|
133
|
+
allow(connection).to receive(:all)
|
134
|
+
.and_return(results)
|
135
|
+
|
136
|
+
expect(subject.first).to be first_result_as_model
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|