guacamole 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +18 -0
  6. data/CONTRIBUTING.md +26 -0
  7. data/Gemfile +16 -0
  8. data/Gemfile.devtools +55 -0
  9. data/Guardfile +13 -0
  10. data/LICENSE +202 -0
  11. data/README.md +130 -0
  12. data/Rakefile +7 -0
  13. data/config/devtools.yml +4 -0
  14. data/config/flay.yml +3 -0
  15. data/config/flog.yml +2 -0
  16. data/config/mutant.yml +3 -0
  17. data/config/reek.yml +104 -0
  18. data/config/rubocop.yml +68 -0
  19. data/config/yardstick.yml +2 -0
  20. data/guacamole.gemspec +28 -0
  21. data/lib/guacamole.rb +19 -0
  22. data/lib/guacamole/collection.rb +246 -0
  23. data/lib/guacamole/configuration.rb +123 -0
  24. data/lib/guacamole/document_model_mapper.rb +95 -0
  25. data/lib/guacamole/model.rb +210 -0
  26. data/lib/guacamole/query.rb +72 -0
  27. data/lib/guacamole/railtie.rb +27 -0
  28. data/lib/guacamole/railtie/database.rake +5 -0
  29. data/lib/guacamole/version.rb +5 -0
  30. data/log/.gitkeep +0 -0
  31. data/spec/acceptance/.gitkeep +0 -0
  32. data/spec/acceptance/basic_spec.rb +112 -0
  33. data/spec/acceptance/config/guacamole.yml +11 -0
  34. data/spec/acceptance/spec_helper.rb +61 -0
  35. data/spec/fabricators/article_fabricator.rb +10 -0
  36. data/spec/fabricators/comment_fabricator.rb +5 -0
  37. data/spec/setup/arangodb.sh +65 -0
  38. data/spec/spec_helper.rb +19 -0
  39. data/spec/unit/.gitkeep +0 -0
  40. data/spec/unit/collection_spec.rb +380 -0
  41. data/spec/unit/configuration_spec.rb +119 -0
  42. data/spec/unit/document_model_mapper_spec.rb +120 -0
  43. data/spec/unit/example_spec.rb +8 -0
  44. data/spec/unit/model_spec.rb +116 -0
  45. data/spec/unit/query_spec.rb +140 -0
  46. data/tasks/adjustments.rake +35 -0
  47. 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,8 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe 'experiment' do
5
+ it 'should work' do
6
+ expect(5).to be 5
7
+ end
8
+ 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