lotus-model 0.0.0 → 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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +6 -0
- data/.yardopts +5 -0
- data/EXAMPLE.md +217 -0
- data/Gemfile +14 -2
- data/README.md +303 -3
- data/Rakefile +17 -1
- data/lib/lotus-model.rb +1 -0
- data/lib/lotus/entity.rb +157 -0
- data/lib/lotus/model.rb +23 -2
- data/lib/lotus/model/adapters/abstract.rb +167 -0
- data/lib/lotus/model/adapters/implementation.rb +111 -0
- data/lib/lotus/model/adapters/memory/collection.rb +132 -0
- data/lib/lotus/model/adapters/memory/command.rb +90 -0
- data/lib/lotus/model/adapters/memory/query.rb +457 -0
- data/lib/lotus/model/adapters/memory_adapter.rb +149 -0
- data/lib/lotus/model/adapters/sql/collection.rb +209 -0
- data/lib/lotus/model/adapters/sql/command.rb +67 -0
- data/lib/lotus/model/adapters/sql/query.rb +615 -0
- data/lib/lotus/model/adapters/sql_adapter.rb +154 -0
- data/lib/lotus/model/mapper.rb +101 -0
- data/lib/lotus/model/mapping.rb +23 -0
- data/lib/lotus/model/mapping/coercer.rb +80 -0
- data/lib/lotus/model/mapping/collection.rb +336 -0
- data/lib/lotus/model/version.rb +4 -1
- data/lib/lotus/repository.rb +620 -0
- data/lotus-model.gemspec +15 -11
- data/test/entity_test.rb +126 -0
- data/test/fixtures.rb +81 -0
- data/test/model/adapters/abstract_test.rb +75 -0
- data/test/model/adapters/implementation_test.rb +22 -0
- data/test/model/adapters/memory/query_test.rb +91 -0
- data/test/model/adapters/memory_adapter_test.rb +1044 -0
- data/test/model/adapters/sql/query_test.rb +121 -0
- data/test/model/adapters/sql_adapter_test.rb +1078 -0
- data/test/model/mapper_test.rb +94 -0
- data/test/model/mapping/coercer_test.rb +27 -0
- data/test/model/mapping/collection_test.rb +82 -0
- data/test/repository_test.rb +283 -0
- data/test/test_helper.rb +30 -0
- data/test/version_test.rb +7 -0
- metadata +109 -11
data/lotus-model.gemspec
CHANGED
@@ -4,20 +4,24 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'lotus/model/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'lotus-model'
|
8
8
|
spec.version = Lotus::Model::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary = %q{
|
12
|
-
spec.description = %q{
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
9
|
+
spec.authors = ['Luca Guidi']
|
10
|
+
spec.email = ['me@lucaguidi.com']
|
11
|
+
spec.summary = %q{A persistence layer for Lotus}
|
12
|
+
spec.description = %q{A persistence framework with entities, repositories, data mapper and query objects}
|
13
|
+
spec.homepage = 'http://lotusrb.org'
|
14
|
+
spec.license = 'MIT'
|
15
15
|
|
16
|
-
spec.files = `git ls-files`.split(
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = [
|
19
|
+
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.
|
22
|
-
spec.
|
21
|
+
spec.add_runtime_dependency 'lotus-utils', '~> 0.1', '> 0.1.0'
|
22
|
+
spec.add_runtime_dependency 'sequel', '~> 4.9'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
25
|
+
spec.add_development_dependency 'minitest', '~> 5'
|
26
|
+
spec.add_development_dependency 'rake', '~> 10'
|
23
27
|
end
|
data/test/entity_test.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Lotus::Entity do
|
4
|
+
before do
|
5
|
+
class Car
|
6
|
+
include Lotus::Entity
|
7
|
+
end
|
8
|
+
|
9
|
+
class Book
|
10
|
+
include Lotus::Entity
|
11
|
+
self.attributes = :title, :author
|
12
|
+
end
|
13
|
+
|
14
|
+
class NonFinctionBook < Book
|
15
|
+
end
|
16
|
+
|
17
|
+
class Camera
|
18
|
+
include Lotus::Entity
|
19
|
+
attr_accessor :analog
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
after do
|
24
|
+
[:Car, :Book, :NonFinctionBook, :Camera].each do |const|
|
25
|
+
Object.send(:remove_const, const)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'attributes' do
|
30
|
+
let(:attributes) { [:id, :model] }
|
31
|
+
|
32
|
+
it 'defines attributes' do
|
33
|
+
Car.send(:attributes=, attributes)
|
34
|
+
Car.send(:attributes).must_equal attributes
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#initialize' do
|
39
|
+
describe 'with defined attributes' do
|
40
|
+
it 'accepts given attributes' do
|
41
|
+
book = Book.new(title: "A Lover's Discourse: Fragments", author: 'Roland Barthes')
|
42
|
+
|
43
|
+
book.instance_variable_get(:@title).must_equal "A Lover's Discourse: Fragments"
|
44
|
+
book.instance_variable_get(:@author).must_equal 'Roland Barthes'
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'ignores unknown attributes' do
|
48
|
+
book = Book.new(unknown: 'x')
|
49
|
+
|
50
|
+
book.instance_variable_get(:@book).must_be_nil
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'accepts given attributes for subclass' do
|
54
|
+
book = NonFinctionBook.new(title: 'Refactoring', author: 'Martin Fowler')
|
55
|
+
|
56
|
+
book.instance_variable_get(:@title).must_equal 'Refactoring'
|
57
|
+
book.instance_variable_get(:@author).must_equal 'Martin Fowler'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'with undefined attributes' do
|
62
|
+
it 'is able to initialize an entity without given attributes' do
|
63
|
+
camera = Camera.new
|
64
|
+
camera.analog.must_be_nil
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'is able to initialize an entity if it has the right accessors' do
|
68
|
+
camera = Camera.new(analog: true)
|
69
|
+
camera.analog.must_equal(true)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "raises an error when the given attributes don't correspond to a known accessor" do
|
73
|
+
-> { Camera.new(digital: true) }.must_raise(NoMethodError)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'accessors' do
|
79
|
+
it 'exposes getters for attributes' do
|
80
|
+
book = Book.new(title: 'High Fidelity')
|
81
|
+
|
82
|
+
book.title.must_equal 'High Fidelity'
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'exposes setters for attributes' do
|
86
|
+
book = Book.new
|
87
|
+
book.title = 'A Man'
|
88
|
+
|
89
|
+
book.instance_variable_get(:@title).must_equal 'A Man'
|
90
|
+
book.title.must_equal 'A Man'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'exposes accessor for id' do
|
94
|
+
book = Book.new
|
95
|
+
book.id.must_be_nil
|
96
|
+
|
97
|
+
book.id = 23
|
98
|
+
book.id.must_equal 23
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#==' do
|
103
|
+
before do
|
104
|
+
@book1 = Book.new
|
105
|
+
@book1.id = 23
|
106
|
+
|
107
|
+
@book2 = Book.new
|
108
|
+
@book2.id = 23
|
109
|
+
|
110
|
+
@book3 = Book.new
|
111
|
+
@car = Car.new
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'returns true if they have the same class and id' do
|
115
|
+
@book1.must_equal @book2
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'returns false if they have the same class but different id' do
|
119
|
+
@book1.wont_equal @book3
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'returns false if they have different class' do
|
123
|
+
@book1.wont_equal @car
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/test/fixtures.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
class User
|
2
|
+
include Lotus::Entity
|
3
|
+
self.attributes = :name, :age
|
4
|
+
end
|
5
|
+
|
6
|
+
class Article
|
7
|
+
include Lotus::Entity
|
8
|
+
self.attributes = :user_id, :unmapped_attribute, :title, :comments_count
|
9
|
+
end
|
10
|
+
|
11
|
+
class UserRepository
|
12
|
+
include Lotus::Repository
|
13
|
+
end
|
14
|
+
|
15
|
+
class ArticleRepository
|
16
|
+
include Lotus::Repository
|
17
|
+
|
18
|
+
def self.rank
|
19
|
+
query do
|
20
|
+
desc(:comments_count)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.by_user(user)
|
25
|
+
query do
|
26
|
+
where(user_id: user.id)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.not_by_user(user)
|
31
|
+
exclude by_user(user)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.rank_by_user(user)
|
35
|
+
rank.by_user(user)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
DB = Sequel.connect(SQLITE_CONNECTION_STRING)
|
40
|
+
|
41
|
+
DB.create_table :users do
|
42
|
+
primary_key :id
|
43
|
+
String :name
|
44
|
+
Integer :age
|
45
|
+
end
|
46
|
+
|
47
|
+
DB.create_table :articles do
|
48
|
+
primary_key :_id
|
49
|
+
Integer :user_id
|
50
|
+
String :s_title
|
51
|
+
String :comments_count # Not an error: we're testing String => Integer coercion
|
52
|
+
String :umapped_column
|
53
|
+
end
|
54
|
+
|
55
|
+
DB.create_table :devices do
|
56
|
+
primary_key :id
|
57
|
+
end
|
58
|
+
|
59
|
+
# DB.dataset_class = Class.new(Sequel::Dataset)
|
60
|
+
|
61
|
+
#FIXME this should be passed by the framework internals.
|
62
|
+
MAPPER = Lotus::Model::Mapper.new do
|
63
|
+
collection :users do
|
64
|
+
entity User
|
65
|
+
|
66
|
+
attribute :id, Integer
|
67
|
+
attribute :name, String
|
68
|
+
attribute :age, Integer
|
69
|
+
end
|
70
|
+
|
71
|
+
collection :articles do
|
72
|
+
entity Article
|
73
|
+
|
74
|
+
attribute :id, Integer, as: :_id
|
75
|
+
attribute :user_id, Integer
|
76
|
+
attribute :title, String, as: 's_title'
|
77
|
+
attribute :comments_count, Integer
|
78
|
+
|
79
|
+
identity :_id
|
80
|
+
end
|
81
|
+
end.load!
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Lotus::Model::Adapters::Abstract do
|
4
|
+
let(:adapter) { Lotus::Model::Adapters::Abstract.new(mapper) }
|
5
|
+
let(:mapper) { Object.new }
|
6
|
+
let(:entity) { Object.new }
|
7
|
+
let(:query) { Object.new }
|
8
|
+
let(:collection) { :collection }
|
9
|
+
|
10
|
+
describe '#persist' do
|
11
|
+
it 'raises error' do
|
12
|
+
->{ adapter.persist(collection, entity) }.must_raise NotImplementedError
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#create' do
|
17
|
+
it 'raises error' do
|
18
|
+
->{ adapter.create(collection, entity) }.must_raise NotImplementedError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#update' do
|
23
|
+
it 'raises error' do
|
24
|
+
->{ adapter.update(collection, entity) }.must_raise NotImplementedError
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#delete' do
|
29
|
+
it 'raises error' do
|
30
|
+
->{ adapter.delete(collection, entity) }.must_raise NotImplementedError
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#all' do
|
35
|
+
it 'raises error' do
|
36
|
+
->{ adapter.all(collection) }.must_raise NotImplementedError
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#find' do
|
41
|
+
it 'raises error' do
|
42
|
+
->{ adapter.find(collection, 1) }.must_raise NotImplementedError
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#first' do
|
47
|
+
it 'raises error' do
|
48
|
+
->{ adapter.first(collection) }.must_raise NotImplementedError
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#last' do
|
53
|
+
it 'raises error' do
|
54
|
+
->{ adapter.last(collection) }.must_raise NotImplementedError
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#clear' do
|
59
|
+
it 'raises error' do
|
60
|
+
->{ adapter.clear(collection) }.must_raise NotImplementedError
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#command' do
|
65
|
+
it 'raises error' do
|
66
|
+
->{ adapter.command(query) }.must_raise NotImplementedError
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#query' do
|
71
|
+
it 'raises error' do
|
72
|
+
->{ adapter.query(collection) }.must_raise NotImplementedError
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Lotus::Model::Adapters::Implementation do
|
4
|
+
before do
|
5
|
+
TestAdapter = Class.new(Lotus::Model::Adapters::Abstract) do
|
6
|
+
include Lotus::Model::Adapters::Implementation
|
7
|
+
end
|
8
|
+
|
9
|
+
mapper = Object.new
|
10
|
+
@adapter = TestAdapter.new(mapper)
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
Object.send(:remove_const, :TestAdapter)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'must implement #_collection' do
|
18
|
+
-> {
|
19
|
+
@adapter.send(:_collection, :x)
|
20
|
+
}.must_raise NotImplementedError
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Lotus::Model::Adapters::Memory::Query do
|
4
|
+
before do
|
5
|
+
MockDataset = Struct.new(:records) do
|
6
|
+
def all
|
7
|
+
records
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
records.to_s
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class MockCollection
|
16
|
+
def deserialize(array)
|
17
|
+
array
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
collection = MockCollection.new
|
22
|
+
@query = Lotus::Model::Adapters::Memory::Query.new(dataset, collection)
|
23
|
+
end
|
24
|
+
|
25
|
+
after do
|
26
|
+
Object.send(:remove_const, :MockDataset)
|
27
|
+
Object.send(:remove_const, :MockCollection)
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:dataset) { MockDataset.new([]) }
|
31
|
+
|
32
|
+
describe '#negate!' do
|
33
|
+
it 'raises an error' do
|
34
|
+
-> { @query.negate! }.must_raise NotImplementedError
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#to_s' do
|
39
|
+
let(:dataset) { MockDataset.new([1, 2, 3]) }
|
40
|
+
|
41
|
+
it 'delegates to the wrapped dataset' do
|
42
|
+
@query.to_s.must_equal dataset.to_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#empty?' do
|
47
|
+
describe "when it's empty" do
|
48
|
+
it 'returns true' do
|
49
|
+
@query.must_be_empty
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "when it's filled with elements" do
|
54
|
+
let(:dataset) { MockDataset.new([1, 2, 3]) }
|
55
|
+
|
56
|
+
it 'returns false' do
|
57
|
+
@query.wont_be_empty
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#any?' do
|
63
|
+
describe "when it's empty" do
|
64
|
+
it 'returns false' do
|
65
|
+
assert !@query.any?
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "when it's filled with elements" do
|
70
|
+
let(:dataset) { MockDataset.new([1, 2, 3]) }
|
71
|
+
|
72
|
+
it 'returns true' do
|
73
|
+
assert @query.any?
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "when a block is passed" do
|
77
|
+
describe "and it doesn't match elements" do
|
78
|
+
it 'returns false' do
|
79
|
+
assert !@query.any? {|e| e > 100 }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "and it matches elements" do
|
84
|
+
it 'returns true' do
|
85
|
+
assert @query.any? {|e| e % 2 == 0 }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,1044 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Lotus::Model::Adapters::MemoryAdapter do
|
4
|
+
before do
|
5
|
+
TestUser = Struct.new(:id, :name, :age) do
|
6
|
+
include Lotus::Entity
|
7
|
+
end
|
8
|
+
|
9
|
+
TestDevice = Struct.new(:id) do
|
10
|
+
include Lotus::Entity
|
11
|
+
end
|
12
|
+
|
13
|
+
@mapper = Lotus::Model::Mapper.new do
|
14
|
+
collection :users do
|
15
|
+
entity TestUser
|
16
|
+
|
17
|
+
attribute :id, Integer
|
18
|
+
attribute :name, String
|
19
|
+
attribute :age, Integer
|
20
|
+
end
|
21
|
+
|
22
|
+
collection :devices do
|
23
|
+
entity TestDevice
|
24
|
+
|
25
|
+
attribute :id, Integer
|
26
|
+
end
|
27
|
+
end.load!
|
28
|
+
|
29
|
+
@adapter = Lotus::Model::Adapters::MemoryAdapter.new(@mapper)
|
30
|
+
end
|
31
|
+
|
32
|
+
after do
|
33
|
+
Object.send(:remove_const, :TestUser)
|
34
|
+
Object.send(:remove_const, :TestDevice)
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:collection) { :users }
|
38
|
+
|
39
|
+
describe 'multiple collections' do
|
40
|
+
it 'create records' do
|
41
|
+
user = TestUser.new
|
42
|
+
device = TestDevice.new
|
43
|
+
|
44
|
+
@adapter.create(:users, user)
|
45
|
+
@adapter.create(:devices, device)
|
46
|
+
|
47
|
+
@adapter.all(:users).must_equal [user]
|
48
|
+
@adapter.all(:devices).must_equal [device]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#persist' do
|
53
|
+
describe 'when the given entity is not persisted' do
|
54
|
+
let(:entity) { TestUser.new }
|
55
|
+
|
56
|
+
it 'stores the record and assigns an id' do
|
57
|
+
@adapter.persist(collection, entity)
|
58
|
+
|
59
|
+
entity.id.wont_be_nil
|
60
|
+
@adapter.find(collection, entity.id).must_equal entity
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'when the given entity is persisted' do
|
65
|
+
before do
|
66
|
+
@adapter.create(collection, entity)
|
67
|
+
end
|
68
|
+
|
69
|
+
let(:entity) { TestUser.new }
|
70
|
+
|
71
|
+
it 'updates the record and leaves untouched the id' do
|
72
|
+
id = entity.id
|
73
|
+
id.wont_be_nil
|
74
|
+
|
75
|
+
entity.name = 'L'
|
76
|
+
@adapter.persist(collection, entity)
|
77
|
+
|
78
|
+
entity.id.must_equal(id)
|
79
|
+
@adapter.find(collection, entity.id).must_equal entity
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '#create' do
|
85
|
+
let(:entity) { TestUser.new }
|
86
|
+
|
87
|
+
it 'stores the record and assigns an id' do
|
88
|
+
@adapter.create(collection, entity)
|
89
|
+
|
90
|
+
entity.id.wont_be_nil
|
91
|
+
@adapter.find(collection, entity.id).must_equal entity
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '#update' do
|
96
|
+
before do
|
97
|
+
@adapter.create(collection, entity)
|
98
|
+
end
|
99
|
+
|
100
|
+
let(:entity) { TestUser.new(id: nil, name: 'L') }
|
101
|
+
|
102
|
+
it 'stores the changes and leave the id untouched' do
|
103
|
+
id = entity.id
|
104
|
+
|
105
|
+
entity.name = 'MG'
|
106
|
+
@adapter.update(collection, entity)
|
107
|
+
|
108
|
+
entity.id.must_equal id
|
109
|
+
@adapter.find(collection, entity.id).must_equal entity
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#delete' do
|
114
|
+
before do
|
115
|
+
@adapter.create(collection, entity)
|
116
|
+
end
|
117
|
+
|
118
|
+
let(:entity) { TestUser.new }
|
119
|
+
|
120
|
+
it 'removes the given identity' do
|
121
|
+
@adapter.delete(collection, entity)
|
122
|
+
@adapter.find(collection, entity.id).must_be_nil
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe '#all' do
|
127
|
+
describe 'when no records are persisted' do
|
128
|
+
before do
|
129
|
+
@adapter.clear(collection)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'returns an empty collection' do
|
133
|
+
@adapter.all(collection).must_be_empty
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe 'when some records are persisted' do
|
138
|
+
before do
|
139
|
+
@adapter.create(collection, entity)
|
140
|
+
end
|
141
|
+
|
142
|
+
let(:entity) { TestUser.new }
|
143
|
+
|
144
|
+
it 'returns all of them' do
|
145
|
+
@adapter.all(collection).must_equal [entity]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe '#find' do
|
151
|
+
before do
|
152
|
+
@adapter.create(collection, entity)
|
153
|
+
@adapter.instance_variable_get(:@collections).fetch(collection).records.store(nil, nil_entity)
|
154
|
+
end
|
155
|
+
|
156
|
+
let(:entity) { TestUser.new }
|
157
|
+
let(:nil_entity) { {id: 0} }
|
158
|
+
|
159
|
+
it 'returns the record by id' do
|
160
|
+
@adapter.find(collection, entity.id).must_equal entity
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'returns nil when the record cannot be found' do
|
164
|
+
@adapter.find(collection, 1_000_000).must_be_nil
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'returns nil when the given id is nil' do
|
168
|
+
@adapter.find(collection, nil).must_be_nil
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe '#first' do
|
173
|
+
describe 'when no records are peristed' do
|
174
|
+
before do
|
175
|
+
@adapter.clear(collection)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'returns nil' do
|
179
|
+
@adapter.first(collection).must_be_nil
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe 'when some records are persisted' do
|
184
|
+
before do
|
185
|
+
@adapter.create(collection, entity1)
|
186
|
+
@adapter.create(collection, entity2)
|
187
|
+
end
|
188
|
+
|
189
|
+
let(:entity1) { TestUser.new }
|
190
|
+
let(:entity2) { TestUser.new }
|
191
|
+
|
192
|
+
it 'returns the first record' do
|
193
|
+
@adapter.first(collection).must_equal entity1
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe '#last' do
|
199
|
+
describe 'when no records are peristed' do
|
200
|
+
before do
|
201
|
+
@adapter.clear(collection)
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'returns nil' do
|
205
|
+
@adapter.last(collection).must_be_nil
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
describe 'when some records are persisted' do
|
210
|
+
before do
|
211
|
+
@adapter.create(collection, entity1)
|
212
|
+
@adapter.create(collection, entity2)
|
213
|
+
end
|
214
|
+
|
215
|
+
let(:entity1) { TestUser.new }
|
216
|
+
let(:entity2) { TestUser.new }
|
217
|
+
|
218
|
+
it 'returns the last record' do
|
219
|
+
@adapter.last(collection).must_equal entity2
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe '#clear' do
|
225
|
+
before do
|
226
|
+
@adapter.create(collection, entity)
|
227
|
+
end
|
228
|
+
|
229
|
+
let(:entity) { TestUser.new }
|
230
|
+
|
231
|
+
it 'removes all the records' do
|
232
|
+
@adapter.clear(collection)
|
233
|
+
@adapter.all(collection).must_be_empty
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'resets the id counter' do
|
237
|
+
@adapter.clear(collection)
|
238
|
+
|
239
|
+
@adapter.create(collection, entity)
|
240
|
+
entity.id.must_equal 1
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe '#query' do
|
245
|
+
before do
|
246
|
+
@adapter.clear(collection)
|
247
|
+
end
|
248
|
+
|
249
|
+
let(:user1) { TestUser.new(name: 'L', age: '32') }
|
250
|
+
let(:user2) { TestUser.new(name: 'MG', age: 31) }
|
251
|
+
|
252
|
+
describe 'where' do
|
253
|
+
describe 'with an empty collection' do
|
254
|
+
it 'returns an empty result set' do
|
255
|
+
result = @adapter.query(collection) do
|
256
|
+
where(id: 23)
|
257
|
+
end.all
|
258
|
+
|
259
|
+
result.must_be_empty
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
describe 'with a filled collection' do
|
264
|
+
before do
|
265
|
+
@adapter.create(collection, user1)
|
266
|
+
@adapter.create(collection, user2)
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'returns selected records' do
|
270
|
+
id = user1.id
|
271
|
+
|
272
|
+
query = Proc.new {
|
273
|
+
where(id: id)
|
274
|
+
}
|
275
|
+
|
276
|
+
result = @adapter.query(collection, &query).all
|
277
|
+
result.must_equal [user1]
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'can use multiple where conditions' do
|
281
|
+
id = user1.id
|
282
|
+
name = user1.name
|
283
|
+
|
284
|
+
query = Proc.new {
|
285
|
+
where(id: id).where(name: name)
|
286
|
+
}
|
287
|
+
|
288
|
+
result = @adapter.query(collection, &query).all
|
289
|
+
result.must_equal [user1]
|
290
|
+
end
|
291
|
+
|
292
|
+
it 'can use multiple where conditions with "and" alias' do
|
293
|
+
id = user1.id
|
294
|
+
name = user1.name
|
295
|
+
|
296
|
+
query = Proc.new {
|
297
|
+
where(id: id).and(name: name)
|
298
|
+
}
|
299
|
+
|
300
|
+
result = @adapter.query(collection, &query).all
|
301
|
+
result.must_equal [user1]
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe 'exclude' do
|
307
|
+
describe 'with an empty collection' do
|
308
|
+
it 'returns an empty result set' do
|
309
|
+
result = @adapter.query(collection) do
|
310
|
+
exclude(id: 23)
|
311
|
+
end.all
|
312
|
+
|
313
|
+
result.must_be_empty
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
describe 'with a filled collection' do
|
318
|
+
before do
|
319
|
+
@adapter.create(collection, user1)
|
320
|
+
@adapter.create(collection, user2)
|
321
|
+
@adapter.create(collection, user3)
|
322
|
+
end
|
323
|
+
|
324
|
+
let(:user3) { TestUser.new(name: 'S', age: 2) }
|
325
|
+
|
326
|
+
it 'returns selected records' do
|
327
|
+
id = user1.id
|
328
|
+
|
329
|
+
query = Proc.new {
|
330
|
+
exclude(id: id)
|
331
|
+
}
|
332
|
+
|
333
|
+
result = @adapter.query(collection, &query).all
|
334
|
+
result.must_equal [user2, user3]
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'can use multiple exclude conditions' do
|
338
|
+
id = user1.id
|
339
|
+
name = user2.name
|
340
|
+
|
341
|
+
query = Proc.new {
|
342
|
+
exclude(id: id).exclude(name: name)
|
343
|
+
}
|
344
|
+
|
345
|
+
$debug = true
|
346
|
+
result = @adapter.query(collection, &query).all
|
347
|
+
result.must_equal [user3]
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'can use multiple exclude conditions with "not" alias' do
|
351
|
+
id = user1.id
|
352
|
+
name = user2.name
|
353
|
+
|
354
|
+
query = Proc.new {
|
355
|
+
self.not(id: id).not(name: name)
|
356
|
+
}
|
357
|
+
|
358
|
+
result = @adapter.query(collection, &query).all
|
359
|
+
result.must_equal [user3]
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
describe 'or' do
|
365
|
+
describe 'with an empty collection' do
|
366
|
+
it 'returns an empty result set' do
|
367
|
+
result = @adapter.query(collection) do
|
368
|
+
where(name: 'L').or(name: 'MG')
|
369
|
+
end.all
|
370
|
+
|
371
|
+
result.must_be_empty
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
describe 'with a filled collection' do
|
376
|
+
before do
|
377
|
+
@adapter.create(collection, user1)
|
378
|
+
@adapter.create(collection, user2)
|
379
|
+
end
|
380
|
+
|
381
|
+
it 'returns selected records' do
|
382
|
+
name1 = user1.name
|
383
|
+
name2 = user2.name
|
384
|
+
|
385
|
+
query = Proc.new {
|
386
|
+
where(name: name1).or(name: name2)
|
387
|
+
}
|
388
|
+
|
389
|
+
result = @adapter.query(collection, &query).all
|
390
|
+
result.must_equal [user1, user2]
|
391
|
+
end
|
392
|
+
|
393
|
+
it 'returns selected records only from the "or" condition' do
|
394
|
+
name2 = user2.name
|
395
|
+
|
396
|
+
query = Proc.new {
|
397
|
+
where(name: 'unknown').or(name: name2)
|
398
|
+
}
|
399
|
+
|
400
|
+
result = @adapter.query(collection, &query).all
|
401
|
+
result.must_equal [user2]
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
describe 'select' do
|
407
|
+
describe 'with an empty collection' do
|
408
|
+
it 'returns an empty result' do
|
409
|
+
result = @adapter.query(collection) do
|
410
|
+
select(:age)
|
411
|
+
end.all
|
412
|
+
|
413
|
+
result.must_be_empty
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
describe 'with a filled collection' do
|
418
|
+
before do
|
419
|
+
@adapter.create(collection, user1)
|
420
|
+
@adapter.create(collection, user2)
|
421
|
+
@adapter.create(collection, user3)
|
422
|
+
end
|
423
|
+
|
424
|
+
let(:user1) { TestUser.new(name: 'L', age: 32) }
|
425
|
+
let(:user3) { TestUser.new(name: 'S') }
|
426
|
+
let(:users) { [user1, user2, user3] }
|
427
|
+
|
428
|
+
it 'returns the selected columnts from all the records' do
|
429
|
+
query = Proc.new {
|
430
|
+
select(:age)
|
431
|
+
}
|
432
|
+
|
433
|
+
result = @adapter.query(collection, &query).all
|
434
|
+
|
435
|
+
users.each do |user|
|
436
|
+
record = result.find {|r| r.age == user.age }
|
437
|
+
record.wont_be_nil
|
438
|
+
record.name.must_be_nil
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
it 'returns only the select of requested records' do
|
443
|
+
name = user2.name
|
444
|
+
|
445
|
+
query = Proc.new {
|
446
|
+
where(name: name).select(:age)
|
447
|
+
}
|
448
|
+
|
449
|
+
result = @adapter.query(collection, &query).all
|
450
|
+
|
451
|
+
record = result.first
|
452
|
+
record.age.must_equal(user2.age)
|
453
|
+
record.name.must_be_nil
|
454
|
+
end
|
455
|
+
|
456
|
+
it 'returns only the multiple select of requested records' do
|
457
|
+
name = user2.name
|
458
|
+
|
459
|
+
query = Proc.new {
|
460
|
+
where(name: name).select(:name, :age)
|
461
|
+
}
|
462
|
+
|
463
|
+
result = @adapter.query(collection, &query).all
|
464
|
+
|
465
|
+
record = result.first
|
466
|
+
record.name.must_equal(user2.name)
|
467
|
+
record.age.must_equal(user2.age)
|
468
|
+
record.id.must_be_nil
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
describe 'order' do
|
474
|
+
describe 'with an empty collection' do
|
475
|
+
it 'returns an empty result set' do
|
476
|
+
result = @adapter.query(collection) do
|
477
|
+
order(:id)
|
478
|
+
end.all
|
479
|
+
|
480
|
+
result.must_be_empty
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
describe 'with a filled collection' do
|
485
|
+
before do
|
486
|
+
@adapter.create(collection, user1)
|
487
|
+
@adapter.create(collection, user2)
|
488
|
+
end
|
489
|
+
|
490
|
+
it 'returns sorted records' do
|
491
|
+
query = Proc.new {
|
492
|
+
order(:id)
|
493
|
+
}
|
494
|
+
|
495
|
+
result = @adapter.query(collection, &query).all
|
496
|
+
result.must_equal [user1, user2]
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
describe 'asc' do
|
502
|
+
describe 'with an empty collection' do
|
503
|
+
it 'returns an empty result set' do
|
504
|
+
result = @adapter.query(collection) do
|
505
|
+
asc(:id)
|
506
|
+
end.all
|
507
|
+
|
508
|
+
result.must_be_empty
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
describe 'with a filled collection' do
|
513
|
+
before do
|
514
|
+
@adapter.create(collection, user1)
|
515
|
+
@adapter.create(collection, user2)
|
516
|
+
end
|
517
|
+
|
518
|
+
it 'returns sorted records' do
|
519
|
+
query = Proc.new {
|
520
|
+
asc(:id)
|
521
|
+
}
|
522
|
+
|
523
|
+
result = @adapter.query(collection, &query).all
|
524
|
+
result.must_equal [user1, user2]
|
525
|
+
end
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
describe 'desc' do
|
530
|
+
describe 'with an empty collection' do
|
531
|
+
it 'returns an empty result set' do
|
532
|
+
result = @adapter.query(collection) do
|
533
|
+
desc(:id)
|
534
|
+
end.all
|
535
|
+
|
536
|
+
result.must_be_empty
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
describe 'with a filled collection' do
|
541
|
+
before do
|
542
|
+
@adapter.create(collection, user1)
|
543
|
+
@adapter.create(collection, user2)
|
544
|
+
end
|
545
|
+
|
546
|
+
it 'returns reverse sorted records' do
|
547
|
+
query = Proc.new {
|
548
|
+
desc(:id)
|
549
|
+
}
|
550
|
+
|
551
|
+
result = @adapter.query(collection, &query).all
|
552
|
+
result.must_equal [user2, user1]
|
553
|
+
end
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
describe 'limit' do
|
558
|
+
describe 'with an empty collection' do
|
559
|
+
it 'returns an empty result set' do
|
560
|
+
result = @adapter.query(collection) do
|
561
|
+
limit(1)
|
562
|
+
end.all
|
563
|
+
|
564
|
+
result.must_be_empty
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
describe 'with a filled collection' do
|
569
|
+
before do
|
570
|
+
@adapter.create(collection, user1)
|
571
|
+
@adapter.create(collection, user2)
|
572
|
+
@adapter.create(collection, TestUser.new(name: user2.name))
|
573
|
+
end
|
574
|
+
|
575
|
+
it 'returns only the number of requested records' do
|
576
|
+
name = user2.name
|
577
|
+
|
578
|
+
query = Proc.new {
|
579
|
+
where(name: name).limit(1)
|
580
|
+
}
|
581
|
+
|
582
|
+
result = @adapter.query(collection, &query).all
|
583
|
+
result.must_equal [user2]
|
584
|
+
end
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
describe 'offset' do
|
589
|
+
describe 'with an empty collection' do
|
590
|
+
it 'returns an empty result set' do
|
591
|
+
result = @adapter.query(collection) do
|
592
|
+
limit(1).offset(1)
|
593
|
+
end.all
|
594
|
+
|
595
|
+
result.must_be_empty
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
describe 'with a filled collection' do
|
600
|
+
before do
|
601
|
+
@adapter.create(collection, user1)
|
602
|
+
@adapter.create(collection, user2)
|
603
|
+
@adapter.create(collection, user3)
|
604
|
+
end
|
605
|
+
|
606
|
+
let(:user3) { TestUser.new(name: user2.name) }
|
607
|
+
|
608
|
+
it 'returns only the number of requested records' do
|
609
|
+
name = user2.name
|
610
|
+
|
611
|
+
query = Proc.new {
|
612
|
+
where(name: name).limit(1).offset(1)
|
613
|
+
}
|
614
|
+
|
615
|
+
result = @adapter.query(collection, &query).all
|
616
|
+
result.must_equal [user3]
|
617
|
+
end
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
describe 'exist?' do
|
622
|
+
describe 'with an empty collection' do
|
623
|
+
it 'returns false' do
|
624
|
+
result = @adapter.query(collection) do
|
625
|
+
where(id: 23)
|
626
|
+
end.exist?
|
627
|
+
|
628
|
+
result.must_equal false
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
describe 'with a filled collection' do
|
633
|
+
before do
|
634
|
+
@adapter.create(collection, user1)
|
635
|
+
@adapter.create(collection, user2)
|
636
|
+
end
|
637
|
+
|
638
|
+
it 'returns true when there are matched records' do
|
639
|
+
id = user1.id
|
640
|
+
|
641
|
+
query = Proc.new {
|
642
|
+
where(id: id)
|
643
|
+
}
|
644
|
+
|
645
|
+
result = @adapter.query(collection, &query).exist?
|
646
|
+
result.must_equal true
|
647
|
+
end
|
648
|
+
|
649
|
+
it 'returns false when there are matched records' do
|
650
|
+
query = Proc.new {
|
651
|
+
where(id: 'unknown')
|
652
|
+
}
|
653
|
+
|
654
|
+
result = @adapter.query(collection, &query).exist?
|
655
|
+
result.must_equal false
|
656
|
+
end
|
657
|
+
end
|
658
|
+
end
|
659
|
+
|
660
|
+
|
661
|
+
describe 'count' do
|
662
|
+
describe 'with an empty collection' do
|
663
|
+
it 'returns 0' do
|
664
|
+
result = @adapter.query(collection) do
|
665
|
+
all
|
666
|
+
end.count
|
667
|
+
|
668
|
+
result.must_equal 0
|
669
|
+
end
|
670
|
+
end
|
671
|
+
|
672
|
+
describe 'with a filled collection' do
|
673
|
+
before do
|
674
|
+
@adapter.create(collection, user1)
|
675
|
+
@adapter.create(collection, user2)
|
676
|
+
end
|
677
|
+
|
678
|
+
it 'returns the count of all the records' do
|
679
|
+
query = Proc.new {
|
680
|
+
all
|
681
|
+
}
|
682
|
+
|
683
|
+
result = @adapter.query(collection, &query).count
|
684
|
+
result.must_equal 2
|
685
|
+
end
|
686
|
+
|
687
|
+
it 'returns the count from an empty query block' do
|
688
|
+
query = Proc.new {
|
689
|
+
}
|
690
|
+
|
691
|
+
result = @adapter.query(collection, &query).count
|
692
|
+
result.must_equal 2
|
693
|
+
end
|
694
|
+
|
695
|
+
it 'returns only the count of requested records' do
|
696
|
+
name = user2.name
|
697
|
+
|
698
|
+
query = Proc.new {
|
699
|
+
where(name: name)
|
700
|
+
}
|
701
|
+
|
702
|
+
result = @adapter.query(collection, &query).count
|
703
|
+
result.must_equal 1
|
704
|
+
end
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
describe 'sum' do
|
709
|
+
describe 'with an empty collection' do
|
710
|
+
it 'returns nil' do
|
711
|
+
result = @adapter.query(collection) do
|
712
|
+
all
|
713
|
+
end.sum(:age)
|
714
|
+
|
715
|
+
result.must_be_nil
|
716
|
+
end
|
717
|
+
end
|
718
|
+
|
719
|
+
describe 'with a filled collection' do
|
720
|
+
before do
|
721
|
+
@adapter.create(collection, user1)
|
722
|
+
@adapter.create(collection, user2)
|
723
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
724
|
+
end
|
725
|
+
|
726
|
+
it 'returns the sum of all the records' do
|
727
|
+
query = Proc.new {
|
728
|
+
all
|
729
|
+
}
|
730
|
+
|
731
|
+
result = @adapter.query(collection, &query).sum(:age)
|
732
|
+
result.must_equal 63
|
733
|
+
end
|
734
|
+
|
735
|
+
it 'returns the sum from an empty query block' do
|
736
|
+
query = Proc.new {
|
737
|
+
}
|
738
|
+
|
739
|
+
result = @adapter.query(collection, &query).sum(:age)
|
740
|
+
result.must_equal 63
|
741
|
+
end
|
742
|
+
|
743
|
+
it 'returns only the sum of requested records' do
|
744
|
+
name = user2.name
|
745
|
+
|
746
|
+
query = Proc.new {
|
747
|
+
where(name: name)
|
748
|
+
}
|
749
|
+
|
750
|
+
result = @adapter.query(collection, &query).sum(:age)
|
751
|
+
result.must_equal 31
|
752
|
+
end
|
753
|
+
end
|
754
|
+
end
|
755
|
+
|
756
|
+
describe 'average' do
|
757
|
+
describe 'with an empty collection' do
|
758
|
+
it 'returns nil' do
|
759
|
+
result = @adapter.query(collection) do
|
760
|
+
all
|
761
|
+
end.average(:age)
|
762
|
+
|
763
|
+
result.must_be_nil
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
describe 'with a filled collection' do
|
768
|
+
before do
|
769
|
+
@adapter.create(collection, user1)
|
770
|
+
@adapter.create(collection, user2)
|
771
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
772
|
+
end
|
773
|
+
|
774
|
+
it 'returns the average of all the records' do
|
775
|
+
query = Proc.new {
|
776
|
+
all
|
777
|
+
}
|
778
|
+
|
779
|
+
result = @adapter.query(collection, &query).average(:age)
|
780
|
+
result.must_equal 31.5
|
781
|
+
end
|
782
|
+
|
783
|
+
it 'returns the average from an empty query block' do
|
784
|
+
query = Proc.new {
|
785
|
+
}
|
786
|
+
|
787
|
+
result = @adapter.query(collection, &query).average(:age)
|
788
|
+
result.must_equal 31.5
|
789
|
+
end
|
790
|
+
|
791
|
+
it 'returns only the average of requested records' do
|
792
|
+
name = user2.name
|
793
|
+
|
794
|
+
query = Proc.new {
|
795
|
+
where(name: name)
|
796
|
+
}
|
797
|
+
|
798
|
+
result = @adapter.query(collection, &query).average(:age)
|
799
|
+
result.must_equal 31.0
|
800
|
+
end
|
801
|
+
end
|
802
|
+
end
|
803
|
+
|
804
|
+
describe 'avg' do
|
805
|
+
describe 'with an empty collection' do
|
806
|
+
it 'returns nil' do
|
807
|
+
result = @adapter.query(collection) do
|
808
|
+
all
|
809
|
+
end.avg(:age)
|
810
|
+
|
811
|
+
result.must_be_nil
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
describe 'with a filled collection' do
|
816
|
+
before do
|
817
|
+
@adapter.create(collection, user1)
|
818
|
+
@adapter.create(collection, user2)
|
819
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
820
|
+
end
|
821
|
+
|
822
|
+
it 'returns the average of all the records' do
|
823
|
+
query = Proc.new {
|
824
|
+
all
|
825
|
+
}
|
826
|
+
|
827
|
+
result = @adapter.query(collection, &query).avg(:age)
|
828
|
+
result.must_equal 31.5
|
829
|
+
end
|
830
|
+
|
831
|
+
it 'returns the average from an empty query block' do
|
832
|
+
query = Proc.new {
|
833
|
+
}
|
834
|
+
|
835
|
+
result = @adapter.query(collection, &query).avg(:age)
|
836
|
+
result.must_equal 31.5
|
837
|
+
end
|
838
|
+
|
839
|
+
it 'returns only the average of requested records' do
|
840
|
+
name = user2.name
|
841
|
+
|
842
|
+
query = Proc.new {
|
843
|
+
where(name: name)
|
844
|
+
}
|
845
|
+
|
846
|
+
result = @adapter.query(collection, &query).avg(:age)
|
847
|
+
result.must_equal 31.0
|
848
|
+
end
|
849
|
+
end
|
850
|
+
end
|
851
|
+
|
852
|
+
describe 'max' do
|
853
|
+
describe 'with an empty collection' do
|
854
|
+
it 'returns nil' do
|
855
|
+
result = @adapter.query(collection) do
|
856
|
+
all
|
857
|
+
end.max(:age)
|
858
|
+
|
859
|
+
result.must_be_nil
|
860
|
+
end
|
861
|
+
end
|
862
|
+
|
863
|
+
describe 'with a filled collection' do
|
864
|
+
before do
|
865
|
+
@adapter.create(collection, user1)
|
866
|
+
@adapter.create(collection, user2)
|
867
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
868
|
+
end
|
869
|
+
|
870
|
+
it 'returns the maximum of all the records' do
|
871
|
+
query = Proc.new {
|
872
|
+
all
|
873
|
+
}
|
874
|
+
|
875
|
+
result = @adapter.query(collection, &query).max(:age)
|
876
|
+
result.must_equal 32
|
877
|
+
end
|
878
|
+
|
879
|
+
it 'returns the maximum from an empty query block' do
|
880
|
+
query = Proc.new {
|
881
|
+
}
|
882
|
+
|
883
|
+
result = @adapter.query(collection, &query).max(:age)
|
884
|
+
result.must_equal 32
|
885
|
+
end
|
886
|
+
|
887
|
+
it 'returns only the maximum of requested records' do
|
888
|
+
name = user2.name
|
889
|
+
|
890
|
+
query = Proc.new {
|
891
|
+
where(name: name)
|
892
|
+
}
|
893
|
+
|
894
|
+
result = @adapter.query(collection, &query).max(:age)
|
895
|
+
result.must_equal 31
|
896
|
+
end
|
897
|
+
end
|
898
|
+
end
|
899
|
+
|
900
|
+
describe 'min' do
|
901
|
+
describe 'with an empty collection' do
|
902
|
+
it 'returns nil' do
|
903
|
+
result = @adapter.query(collection) do
|
904
|
+
all
|
905
|
+
end.min(:age)
|
906
|
+
|
907
|
+
result.must_be_nil
|
908
|
+
end
|
909
|
+
end
|
910
|
+
|
911
|
+
describe 'with a filled collection' do
|
912
|
+
before do
|
913
|
+
@adapter.create(collection, user1)
|
914
|
+
@adapter.create(collection, user2)
|
915
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
916
|
+
end
|
917
|
+
|
918
|
+
it 'returns the minimum of all the records' do
|
919
|
+
query = Proc.new {
|
920
|
+
all
|
921
|
+
}
|
922
|
+
|
923
|
+
result = @adapter.query(collection, &query).min(:age)
|
924
|
+
result.must_equal 31
|
925
|
+
end
|
926
|
+
|
927
|
+
it 'returns the minimum from an empty query block' do
|
928
|
+
query = Proc.new {
|
929
|
+
}
|
930
|
+
|
931
|
+
result = @adapter.query(collection, &query).min(:age)
|
932
|
+
result.must_equal 31
|
933
|
+
end
|
934
|
+
|
935
|
+
it 'returns only the minimum of requested records' do
|
936
|
+
name = user1.name
|
937
|
+
|
938
|
+
query = Proc.new {
|
939
|
+
where(name: name)
|
940
|
+
}
|
941
|
+
|
942
|
+
result = @adapter.query(collection, &query).min(:age)
|
943
|
+
result.must_equal 32
|
944
|
+
end
|
945
|
+
end
|
946
|
+
end
|
947
|
+
|
948
|
+
describe 'interval' do
|
949
|
+
describe 'with an empty collection' do
|
950
|
+
it 'returns nil' do
|
951
|
+
result = @adapter.query(collection) do
|
952
|
+
all
|
953
|
+
end.interval(:age)
|
954
|
+
|
955
|
+
result.must_be_nil
|
956
|
+
end
|
957
|
+
end
|
958
|
+
|
959
|
+
describe 'with a filled collection' do
|
960
|
+
before do
|
961
|
+
@adapter.create(collection, user1)
|
962
|
+
@adapter.create(collection, user2)
|
963
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
964
|
+
end
|
965
|
+
|
966
|
+
it 'returns the interval of all the records' do
|
967
|
+
query = Proc.new {
|
968
|
+
all
|
969
|
+
}
|
970
|
+
|
971
|
+
result = @adapter.query(collection, &query).interval(:age)
|
972
|
+
result.must_equal 1
|
973
|
+
end
|
974
|
+
|
975
|
+
it 'returns the interval from an empty query block' do
|
976
|
+
query = Proc.new {
|
977
|
+
}
|
978
|
+
|
979
|
+
result = @adapter.query(collection, &query).interval(:age)
|
980
|
+
result.must_equal 1
|
981
|
+
end
|
982
|
+
|
983
|
+
it 'returns only the interval of requested records' do
|
984
|
+
name = user1.name
|
985
|
+
|
986
|
+
query = Proc.new {
|
987
|
+
where(name: name)
|
988
|
+
}
|
989
|
+
|
990
|
+
result = @adapter.query(collection, &query).interval(:age)
|
991
|
+
result.must_equal 0
|
992
|
+
end
|
993
|
+
end
|
994
|
+
end
|
995
|
+
|
996
|
+
describe 'range' do
|
997
|
+
describe 'with an empty collection' do
|
998
|
+
it 'returns nil' do
|
999
|
+
result = @adapter.query(collection) do
|
1000
|
+
all
|
1001
|
+
end.range(:age)
|
1002
|
+
|
1003
|
+
result.must_equal nil..nil
|
1004
|
+
end
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
describe 'with a filled collection' do
|
1008
|
+
before do
|
1009
|
+
@adapter.create(collection, user1)
|
1010
|
+
@adapter.create(collection, user2)
|
1011
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
it 'returns the range of all the records' do
|
1015
|
+
query = Proc.new {
|
1016
|
+
all
|
1017
|
+
}
|
1018
|
+
|
1019
|
+
result = @adapter.query(collection, &query).range(:age)
|
1020
|
+
result.must_equal 31..32
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
it 'returns the range from an empty query block' do
|
1024
|
+
query = Proc.new {
|
1025
|
+
}
|
1026
|
+
|
1027
|
+
result = @adapter.query(collection, &query).range(:age)
|
1028
|
+
result.must_equal 31..32
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
it 'returns only the range of requested records' do
|
1032
|
+
name = user2.name
|
1033
|
+
|
1034
|
+
query = Proc.new {
|
1035
|
+
where(name: name)
|
1036
|
+
}
|
1037
|
+
|
1038
|
+
result = @adapter.query(collection, &query).range(:age)
|
1039
|
+
result.must_equal 31..31
|
1040
|
+
end
|
1041
|
+
end
|
1042
|
+
end
|
1043
|
+
end
|
1044
|
+
end
|