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.
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