rom-relation 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.
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +21 -0
- data/.yardopts +4 -0
- data/Gemfile +24 -0
- data/Gemfile.devtools +55 -0
- data/Guardfile +25 -0
- data/LICENSE +20 -0
- data/README.md +21 -0
- data/Rakefile +4 -0
- data/TODO.md +4 -0
- data/config/devtools.yml +2 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +8 -0
- data/config/reek.yml +97 -0
- data/config/rubocop.yml +41 -0
- data/lib/rom/environment.rb +133 -0
- data/lib/rom/mapping/definition.rb +127 -0
- data/lib/rom/mapping.rb +81 -0
- data/lib/rom/relation.rb +339 -0
- data/lib/rom/repository.rb +62 -0
- data/lib/rom/schema/definition/relation/base.rb +25 -0
- data/lib/rom/schema/definition/relation.rb +44 -0
- data/lib/rom/schema/definition.rb +82 -0
- data/lib/rom/schema.rb +49 -0
- data/lib/rom/support/axiom/adapter/data_objects.rb +39 -0
- data/lib/rom/support/axiom/adapter/memory.rb +25 -0
- data/lib/rom/support/axiom/adapter/postgres.rb +19 -0
- data/lib/rom/support/axiom/adapter/sqlite3.rb +19 -0
- data/lib/rom/support/axiom/adapter.rb +100 -0
- data/lib/rom/version.rb +7 -0
- data/lib/rom-relation.rb +45 -0
- data/rom-relation.gemspec +26 -0
- data/spec/integration/environment_setup_spec.rb +22 -0
- data/spec/integration/mapping_relations_spec.rb +64 -0
- data/spec/integration/schema_definition_spec.rb +94 -0
- data/spec/shared/unit/environment_context.rb +6 -0
- data/spec/shared/unit/relation_context.rb +25 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/support/helper.rb +17 -0
- data/spec/support/test_mapper.rb +23 -0
- data/spec/unit/rom/environment/class_methods/setup_spec.rb +25 -0
- data/spec/unit/rom/environment/element_reader_spec.rb +23 -0
- data/spec/unit/rom/environment/mapping_spec.rb +26 -0
- data/spec/unit/rom/environment/repository_spec.rb +21 -0
- data/spec/unit/rom/environment/schema_spec.rb +33 -0
- data/spec/unit/rom/mapping/class_methods/build_spec.rb +77 -0
- data/spec/unit/rom/relation/class_methods/build_spec.rb +19 -0
- data/spec/unit/rom/relation/delete_spec.rb +15 -0
- data/spec/unit/rom/relation/drop_spec.rb +11 -0
- data/spec/unit/rom/relation/each_spec.rb +23 -0
- data/spec/unit/rom/relation/first_spec.rb +19 -0
- data/spec/unit/rom/relation/inject_mapper_spec.rb +17 -0
- data/spec/unit/rom/relation/insert_spec.rb +13 -0
- data/spec/unit/rom/relation/last_spec.rb +19 -0
- data/spec/unit/rom/relation/one_spec.rb +49 -0
- data/spec/unit/rom/relation/replace_spec.rb +13 -0
- data/spec/unit/rom/relation/restrict_spec.rb +25 -0
- data/spec/unit/rom/relation/sort_by_spec.rb +25 -0
- data/spec/unit/rom/relation/take_spec.rb +11 -0
- data/spec/unit/rom/relation/to_a_spec.rb +20 -0
- data/spec/unit/rom/relation/update_spec.rb +25 -0
- data/spec/unit/rom/repository/class_methods/build_spec.rb +27 -0
- data/spec/unit/rom/repository/element_reader_spec.rb +21 -0
- data/spec/unit/rom/repository/element_writer_spec.rb +18 -0
- data/spec/unit/rom/schema/class_methods/build_spec.rb +103 -0
- metadata +249 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Mapping, '.build' do
|
|
6
|
+
let(:header) { [[:id, Integer], [:user_name, String], [:age, Integer], [:email, String]] }
|
|
7
|
+
let(:relation) { Axiom::Relation::Base.new(:users, header) }
|
|
8
|
+
let(:model) { mock_model(:id, :name, :email) }
|
|
9
|
+
let(:env) { Environment.setup(test: 'memory://test') }
|
|
10
|
+
let(:schema) { Hash[users: relation] }
|
|
11
|
+
|
|
12
|
+
context 'when attribute mapping is used' do
|
|
13
|
+
subject { env }
|
|
14
|
+
|
|
15
|
+
let(:mapper) { subject[:users].mapper }
|
|
16
|
+
|
|
17
|
+
before do
|
|
18
|
+
user_model = model
|
|
19
|
+
|
|
20
|
+
Mapping.build(env, schema) do
|
|
21
|
+
users do
|
|
22
|
+
model user_model
|
|
23
|
+
|
|
24
|
+
map :id, :email
|
|
25
|
+
map :user_name, to: :name
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'registers rom relation' do
|
|
31
|
+
expect(subject[:users]).to be_instance_of(Relation)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'builds rom mapper' do
|
|
35
|
+
expect(mapper.header.map(&:name)).to eql([:id, :email, :name])
|
|
36
|
+
|
|
37
|
+
# TODO: introduce new interface in rom-mapper to make this simpler
|
|
38
|
+
expect(mapper.header.map { |a| a.field.type }).to eql([
|
|
39
|
+
Axiom::Types::Integer, Axiom::Types::String, Axiom::Types::String
|
|
40
|
+
])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'sets up the model' do
|
|
44
|
+
object = mapper.new_object(id: 1, name: 'Jane', email: 'jane@rom.org')
|
|
45
|
+
expect(object).to be_instance_of(model)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
context 'when custom mapper is inject' do
|
|
50
|
+
subject { env }
|
|
51
|
+
|
|
52
|
+
fake(:test_mapper) { TestMapper }
|
|
53
|
+
|
|
54
|
+
before do
|
|
55
|
+
custom_mapper = test_mapper
|
|
56
|
+
Mapping.build(env, schema) { users { mapper(custom_mapper) } }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'sets the custom mapper' do
|
|
60
|
+
stub(test_mapper).call(relation) { relation }
|
|
61
|
+
|
|
62
|
+
expect(subject[:users].mapper).to be(test_mapper)
|
|
63
|
+
|
|
64
|
+
expect(test_mapper).to have_received.call(relation)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
context 'when unknown relation name is used' do
|
|
69
|
+
subject { described_class.build(env, schema) { not_here(1, 'a') {} } }
|
|
70
|
+
|
|
71
|
+
it 'raises error' do
|
|
72
|
+
expect { subject }.to raise_error(
|
|
73
|
+
NoMethodError, /undefined method `not_here'/
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '.build' do
|
|
6
|
+
subject { described_class.build(relation, mapper) }
|
|
7
|
+
|
|
8
|
+
fake(:relation) { Axiom::Relation }
|
|
9
|
+
fake(:mapper) { Mapper }
|
|
10
|
+
|
|
11
|
+
let(:mapped_relation) { mock('mapped_relation') }
|
|
12
|
+
|
|
13
|
+
before do
|
|
14
|
+
stub(mapper).call(relation) { mapped_relation }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
its(:relation) { should be(mapped_relation) }
|
|
18
|
+
its(:mapper) { should be(mapper) }
|
|
19
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '#delete' do
|
|
6
|
+
include_context 'Relation'
|
|
7
|
+
|
|
8
|
+
subject { relation.delete(john) }
|
|
9
|
+
|
|
10
|
+
it { should be_instance_of(Relation) }
|
|
11
|
+
|
|
12
|
+
it 'deletes tuples from the relation' do
|
|
13
|
+
should_not include(john)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '#each' do
|
|
6
|
+
include_context 'Relation'
|
|
7
|
+
|
|
8
|
+
context 'with a block' do
|
|
9
|
+
it 'yields objects' do
|
|
10
|
+
retval = relation.each do |tuple|
|
|
11
|
+
expect(tuple).to be_instance_of(model)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
expect(retval).to be(relation)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
context 'without a block' do
|
|
19
|
+
it 'returns enumerator' do
|
|
20
|
+
expect(relation.each).to be_instance_of(Enumerator)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '#first' do
|
|
6
|
+
include_context 'Relation'
|
|
7
|
+
|
|
8
|
+
context 'when limit is not provided' do
|
|
9
|
+
it 'returns first object' do
|
|
10
|
+
expect(relation.first.to_a).to eql([john])
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context 'when limit is provided' do
|
|
15
|
+
it 'returns first n-objects' do
|
|
16
|
+
expect(relation.first(2).to_a).to eql([john, jane])
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '#inject_mapper' do
|
|
6
|
+
subject(:relation) { described_class.new([], mapper) }
|
|
7
|
+
|
|
8
|
+
fake(:mapper)
|
|
9
|
+
fake(:other_mapper) { Mapper }
|
|
10
|
+
|
|
11
|
+
it 'returns new relation with injected new mapper' do
|
|
12
|
+
other_relation = relation.inject_mapper(other_mapper)
|
|
13
|
+
|
|
14
|
+
expect(other_relation.relation).to be(relation.relation)
|
|
15
|
+
expect(other_relation.mapper).to be(other_mapper)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '#insert' do
|
|
6
|
+
include_context 'Relation'
|
|
7
|
+
|
|
8
|
+
let(:user) { model.new(name: 'Piotr') }
|
|
9
|
+
|
|
10
|
+
it 'inserts object into relation' do
|
|
11
|
+
expect(relation.insert(user).to_a).to include(user)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '#first' do
|
|
6
|
+
include_context 'Relation'
|
|
7
|
+
|
|
8
|
+
context 'when limit is not provided' do
|
|
9
|
+
it 'returns last object' do
|
|
10
|
+
expect(relation.last.to_a).to eql([jade])
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context 'when limit is provided' do
|
|
15
|
+
it 'returns last n-objects' do
|
|
16
|
+
expect(relation.last(2).to_a).to eql([jack, jade])
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '#one' do
|
|
6
|
+
include_context 'Relation'
|
|
7
|
+
|
|
8
|
+
it 'limits the underlying relation' do
|
|
9
|
+
stub(relation).take(2) { [john] }
|
|
10
|
+
expect(relation.one).to eql(john)
|
|
11
|
+
relation.should have_received.take(2)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context 'when no block is given' do
|
|
15
|
+
context 'when one tuple is returned' do
|
|
16
|
+
it 'returns one object' do
|
|
17
|
+
expect(relation.restrict(name: 'John').one).to eql(john)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context 'when no tuple is returned' do
|
|
22
|
+
it 'raises NoTuplesError' do
|
|
23
|
+
expect { relation.restrict(name: 'unknown').one }.to raise_error(NoTuplesError)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'when more than one tuple is returned' do
|
|
28
|
+
let(:header) { [[:id, Integer], [:name, String]] }
|
|
29
|
+
let(:users) { Axiom::Relation.new(header, [[1, 'Jane'], [2, 'Jane']]) }
|
|
30
|
+
let(:model) { mock_model(:id, :name) }
|
|
31
|
+
|
|
32
|
+
it 'raises ManyTuplesError' do
|
|
33
|
+
expect { relation.restrict(name: 'Jane').one }.to raise_error(ManyTuplesError)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context 'when a block is given' do
|
|
39
|
+
let(:block) { ->() { raise error } }
|
|
40
|
+
|
|
41
|
+
context 'when no tuple is returned' do
|
|
42
|
+
let(:error) { Class.new(StandardError) }
|
|
43
|
+
|
|
44
|
+
it 'invokes the block' do
|
|
45
|
+
expect { relation.restrict(name: 'unknown').one(&block) }.to raise_error(error)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '#replace' do
|
|
6
|
+
subject(:relation) { described_class.new(users, mapper) }
|
|
7
|
+
|
|
8
|
+
include_context 'Relation'
|
|
9
|
+
|
|
10
|
+
it 'replaces the relation with a new one' do
|
|
11
|
+
expect(relation.replace([jane]).to_a).to eq([jane])
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '#restrict' do
|
|
6
|
+
include_context 'Relation'
|
|
7
|
+
|
|
8
|
+
share_examples_for 'restricted relation' do
|
|
9
|
+
specify do
|
|
10
|
+
should eq([jane])
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context 'with condition hash' do
|
|
15
|
+
subject { relation.restrict(name: 'Jane').to_a }
|
|
16
|
+
|
|
17
|
+
it_behaves_like 'restricted relation'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context 'with a block' do
|
|
21
|
+
subject { relation.restrict { |r| r.name.eq('Jane') }.to_a }
|
|
22
|
+
|
|
23
|
+
it_behaves_like 'restricted relation'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '#sort_by' do
|
|
6
|
+
include_context 'Relation'
|
|
7
|
+
|
|
8
|
+
share_examples_for 'sorted relation' do
|
|
9
|
+
specify do
|
|
10
|
+
should eql([jack, jade, jane, john])
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context 'with a list of attribute names' do
|
|
15
|
+
subject { relation.sort_by([:name]).to_a }
|
|
16
|
+
|
|
17
|
+
it_behaves_like 'sorted relation'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context 'with a block' do
|
|
21
|
+
subject { relation.sort_by { |r| [r.name] }.to_a }
|
|
22
|
+
|
|
23
|
+
it_behaves_like 'sorted relation'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '#to_a' do
|
|
6
|
+
subject(:relation) { described_class.new(axiom_relation, mapper) }
|
|
7
|
+
|
|
8
|
+
let(:axiom_relation) { [1, 2] }
|
|
9
|
+
let(:loaded_objects) { %w(1 2) }
|
|
10
|
+
fake(:mapper)
|
|
11
|
+
|
|
12
|
+
before do
|
|
13
|
+
stub(mapper).load(1) { '1' }
|
|
14
|
+
stub(mapper).load(2) { '2' }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'gets all tuples and loads them via mapper' do
|
|
18
|
+
expect(relation.to_a).to eql(loaded_objects)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Relation, '#update' do
|
|
6
|
+
include_context 'Relation'
|
|
7
|
+
|
|
8
|
+
subject { relation.update(john, old_tuple) }
|
|
9
|
+
|
|
10
|
+
let!(:old_tuple) { relation.mapper.dump(john) }
|
|
11
|
+
|
|
12
|
+
it { should be_instance_of(Relation) }
|
|
13
|
+
|
|
14
|
+
before do
|
|
15
|
+
john.name = 'John Doe'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'replaces old object with the new one' do
|
|
19
|
+
expect(subject.restrict(name: 'John Doe').one).to eq(john)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'removes old object' do
|
|
23
|
+
expect(subject.restrict(name: 'John').count).to be(0)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Repository, '.build' do
|
|
6
|
+
subject { described_class.build(name, uri) }
|
|
7
|
+
|
|
8
|
+
let(:name) { :test }
|
|
9
|
+
|
|
10
|
+
context 'with a registered uri scheme' do
|
|
11
|
+
let(:uri) { Addressable::URI.parse('memory://test') }
|
|
12
|
+
|
|
13
|
+
it { should be_instance_of(described_class) }
|
|
14
|
+
|
|
15
|
+
its(:name) { should be(name) }
|
|
16
|
+
its(:adapter) { should eq(Axiom::Adapter.build(uri)) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context 'with an unregistered uri scheme' do
|
|
20
|
+
let(:uri) { Addressable::URI.parse('unregistered://test') }
|
|
21
|
+
let(:msg) { "#{uri.scheme.inspect} is no registered uri scheme" }
|
|
22
|
+
|
|
23
|
+
specify do
|
|
24
|
+
expect { subject }.to raise_error(Axiom::UnknownAdapterError, msg)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Repository, '#[]' do
|
|
6
|
+
subject { object[relation_name] }
|
|
7
|
+
|
|
8
|
+
let(:object) { described_class.build(name, uri) }
|
|
9
|
+
let(:name) { :bigdata }
|
|
10
|
+
let(:uri) { Addressable::URI.parse('memory://test') }
|
|
11
|
+
|
|
12
|
+
let(:relation) { Axiom::Relation::Base.new(relation_name, header) }
|
|
13
|
+
let(:relation_name) { :test }
|
|
14
|
+
let(:header) { [] }
|
|
15
|
+
|
|
16
|
+
before do
|
|
17
|
+
object[relation_name] = relation
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it { should eq(relation) }
|
|
21
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Repository, '#[]=' do
|
|
6
|
+
subject { object[:users] }
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
object[:users] = relation
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
let(:object) { Repository.build(:test, Addressable::URI.parse('memory://test')) }
|
|
13
|
+
let(:relation) { Axiom::Relation::Base.new(:users, []) }
|
|
14
|
+
|
|
15
|
+
it { should eq(relation) }
|
|
16
|
+
|
|
17
|
+
it { should be_instance_of(Axiom::Relation::Variable) }
|
|
18
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Schema, '.build' do
|
|
6
|
+
include_context 'Environment'
|
|
7
|
+
|
|
8
|
+
let(:registry) { Hash[test: repository] }
|
|
9
|
+
|
|
10
|
+
let(:keys) { [:id] }
|
|
11
|
+
|
|
12
|
+
let(:schema) {
|
|
13
|
+
key_args = keys
|
|
14
|
+
|
|
15
|
+
described_class.build(registry) do
|
|
16
|
+
base_relation :users do
|
|
17
|
+
repository :test
|
|
18
|
+
|
|
19
|
+
attribute :id, Integer
|
|
20
|
+
attribute :name, String
|
|
21
|
+
|
|
22
|
+
key(*key_args)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let(:header) {
|
|
28
|
+
Axiom::Relation::Header.coerce([[:id, Integer], [:name, String]], keys: keys)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let(:relation) {
|
|
32
|
+
Axiom::Relation::Base.new(:users, header)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fake(:repository)
|
|
36
|
+
fake(:gateway) { Axiom::Relation }
|
|
37
|
+
|
|
38
|
+
before do
|
|
39
|
+
stub(repository).[]=(:users, relation) { gateway }
|
|
40
|
+
stub(repository).[](:users) { gateway }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.it_registers_relation
|
|
44
|
+
it 'registers base relation in the repository' do
|
|
45
|
+
expect(subject[:users]).to be(gateway)
|
|
46
|
+
expect(repository).to have_received.[]=(:users, relation)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
context 'defining base relations' do
|
|
51
|
+
subject { schema }
|
|
52
|
+
|
|
53
|
+
context 'with a single key' do
|
|
54
|
+
it_registers_relation
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context 'with multiple keys' do
|
|
58
|
+
let(:keys) { [:id, :name] }
|
|
59
|
+
|
|
60
|
+
it_registers_relation
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context 'defining relations' do
|
|
65
|
+
subject do
|
|
66
|
+
schema.call do
|
|
67
|
+
relation :restricted_users do
|
|
68
|
+
users.restrict(name: 'Jane')
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'registers restricted relation' do
|
|
74
|
+
stub(gateway).restrict(name: 'Jane') { gateway }
|
|
75
|
+
|
|
76
|
+
expect(subject[:restricted_users]).to be(gateway)
|
|
77
|
+
|
|
78
|
+
expect(gateway).to have_received.restrict(name: 'Jane')
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
context 'when invalid relation name is used' do
|
|
82
|
+
subject do
|
|
83
|
+
schema.call do
|
|
84
|
+
relation :restricted_users do
|
|
85
|
+
not_here.restrict
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'raises error' do
|
|
91
|
+
expect { subject }.to raise_error(
|
|
92
|
+
NameError, /method `not_here'/
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context 'without block' do
|
|
99
|
+
subject { Schema.build({}) }
|
|
100
|
+
|
|
101
|
+
it { should be_instance_of(Schema) }
|
|
102
|
+
end
|
|
103
|
+
end
|