rom-repository 0.2.0 → 0.3.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 +5 -5
- data/CHANGELOG.md +24 -0
- data/Gemfile +21 -5
- data/README.md +6 -110
- data/lib/rom/repository/changeset/create.rb +26 -0
- data/lib/rom/repository/changeset/pipe.rb +40 -0
- data/lib/rom/repository/changeset/update.rb +82 -0
- data/lib/rom/repository/changeset.rb +99 -0
- data/lib/rom/repository/class_interface.rb +142 -0
- data/lib/rom/repository/command_compiler.rb +214 -0
- data/lib/rom/repository/command_proxy.rb +22 -0
- data/lib/rom/repository/header_builder.rb +13 -16
- data/lib/rom/repository/mapper_builder.rb +7 -14
- data/lib/rom/repository/{loading_proxy → relation_proxy}/wrap.rb +7 -7
- data/lib/rom/repository/relation_proxy.rb +225 -0
- data/lib/rom/repository/root.rb +110 -0
- data/lib/rom/repository/struct_attributes.rb +46 -0
- data/lib/rom/repository/struct_builder.rb +31 -14
- data/lib/rom/repository/version.rb +1 -1
- data/lib/rom/repository.rb +192 -31
- data/lib/rom/struct.rb +13 -8
- data/rom-repository.gemspec +9 -10
- data/spec/integration/changeset_spec.rb +86 -0
- data/spec/integration/command_macros_spec.rb +175 -0
- data/spec/integration/command_spec.rb +224 -0
- data/spec/integration/multi_adapter_spec.rb +3 -3
- data/spec/integration/repository_spec.rb +97 -2
- data/spec/integration/root_repository_spec.rb +88 -0
- data/spec/shared/database.rb +47 -3
- data/spec/shared/mappers.rb +35 -0
- data/spec/shared/models.rb +41 -0
- data/spec/shared/plugins.rb +66 -0
- data/spec/shared/relations.rb +76 -0
- data/spec/shared/repo.rb +38 -17
- data/spec/shared/seeds.rb +19 -0
- data/spec/spec_helper.rb +4 -1
- data/spec/support/mapper_registry.rb +1 -3
- data/spec/unit/changeset_spec.rb +58 -0
- data/spec/unit/header_builder_spec.rb +34 -35
- data/spec/unit/relation_proxy_spec.rb +170 -0
- data/spec/unit/sql/relation_spec.rb +5 -5
- data/spec/unit/struct_builder_spec.rb +7 -4
- data/spec/unit/struct_spec.rb +22 -0
- metadata +38 -41
- data/lib/rom/plugins/relation/key_inference.rb +0 -31
- data/lib/rom/repository/loading_proxy/combine.rb +0 -158
- data/lib/rom/repository/loading_proxy.rb +0 -182
- data/spec/unit/loading_proxy_spec.rb +0 -147
@@ -0,0 +1,170 @@
|
|
1
|
+
RSpec.describe 'loading proxy' do
|
2
|
+
include_context 'database'
|
3
|
+
include_context 'relations'
|
4
|
+
include_context 'repo'
|
5
|
+
include_context 'structs'
|
6
|
+
include_context 'seeds'
|
7
|
+
|
8
|
+
let(:users) do
|
9
|
+
ROM::Repository::RelationProxy.new(rom.relation(:users), name: :users)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:tasks) do
|
13
|
+
ROM::Repository::RelationProxy.new(rom.relation(:tasks), name: :tasks)
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:tags) do
|
17
|
+
ROM::Repository::RelationProxy.new(rom.relation(:tags), name: :tags)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#each' do
|
21
|
+
it 'yields loaded structs' do
|
22
|
+
result = []
|
23
|
+
|
24
|
+
users.each { |user| result << user }
|
25
|
+
|
26
|
+
expect(result).to eql([jane, joe])
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns an enumerator when block is not given' do
|
30
|
+
expect(users.each.to_a).to eql([jane, joe])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#map_with/#as' do
|
35
|
+
context 'with custom mappers' do
|
36
|
+
before do
|
37
|
+
configuration.mappers do
|
38
|
+
register :users, {
|
39
|
+
name_list: -> users { users.map { |u| u[:name] } },
|
40
|
+
upcase_names: -> names { names.map(&:upcase) },
|
41
|
+
identity: -> users { users }
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'sends the relation through custom mappers' do
|
47
|
+
expect(users.map_with(:name_list, :upcase_names).to_a).to match_array(%w(JANE JOE))
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'does not use the default(ROM::Struct) mapper' do
|
51
|
+
expect(users.map_with(:identity).to_a).to match_array(
|
52
|
+
[{ id: 1, name: 'Jane' }, {id: 2, name: 'Joe' }]
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'setting custom model type' do
|
58
|
+
let(:user_type) do
|
59
|
+
Class.new(Dry::Types::Struct) do
|
60
|
+
attribute :id, Dry::Types['strict.int']
|
61
|
+
attribute :name, Dry::Types['strict.string']
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
let(:custom_users) { users.as(user_type) }
|
66
|
+
|
67
|
+
it 'instantiates custom model' do
|
68
|
+
expect(custom_users.where(name: 'Jane').one).to be_instance_of(user_type)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'retrieving a single struct' do
|
74
|
+
describe '#first' do
|
75
|
+
it 'returns exactly one struct' do
|
76
|
+
expect(users.first).to eql(jane)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#one' do
|
81
|
+
it 'returns exactly one struct' do
|
82
|
+
expect(users.find(id: 1).one).to eql(jane)
|
83
|
+
|
84
|
+
expect(users.find(id: 3).one).to be(nil)
|
85
|
+
|
86
|
+
expect { users.find(id: [1,2]).one }.to raise_error(ROM::TupleCountMismatchError)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe '#one!' do
|
91
|
+
it 'returns exactly one struct' do
|
92
|
+
expect(users.find(id: 1).one!).to eql(jane)
|
93
|
+
|
94
|
+
expect { users.find(id: [1, 2]).one! }.to raise_error(ROM::TupleCountMismatchError)
|
95
|
+
expect { users.find(id: [3]).one! }.to raise_error(ROM::TupleCountMismatchError)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '#to_ast' do
|
101
|
+
it 'returns valid ast for a single relation' do
|
102
|
+
expect(users.to_ast).to eql(
|
103
|
+
[:relation, [
|
104
|
+
:users,
|
105
|
+
{ dataset: :users },
|
106
|
+
[:header, [[:attribute, :id], [:attribute, :name]]]]
|
107
|
+
]
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'returns valid ast for a combined relation' do
|
112
|
+
relation = users.combine(many: { user_tasks: [tasks, id: :user_id] })
|
113
|
+
|
114
|
+
expect(relation.to_ast).to eql(
|
115
|
+
[:relation, [
|
116
|
+
:users,
|
117
|
+
{ dataset: :users },
|
118
|
+
[:header, [
|
119
|
+
[:attribute, :id],
|
120
|
+
[:attribute, :name],
|
121
|
+
[:relation, [
|
122
|
+
:tasks,
|
123
|
+
{ dataset: :tasks, keys: { id: :user_id }, combine_type: :many, combine_name: :user_tasks },
|
124
|
+
[:header, [[:attribute, :id], [:attribute, :user_id], [:attribute, :title]]]
|
125
|
+
]]
|
126
|
+
]
|
127
|
+
]]]
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'returns valid ast for a wrapped relation' do
|
132
|
+
relation = tags.wrap_parent(task: tasks)
|
133
|
+
|
134
|
+
expect(relation.to_ast).to eql(
|
135
|
+
[:relation, [
|
136
|
+
:tags,
|
137
|
+
{ dataset: :tags },
|
138
|
+
[:header, [
|
139
|
+
[:attribute, :id],
|
140
|
+
[:attribute, :task_id],
|
141
|
+
[:attribute, :name],
|
142
|
+
[:relation, [
|
143
|
+
:tasks,
|
144
|
+
{ dataset: :tasks, keys: { id: :task_id }, wrap: true, combine_name: :task },
|
145
|
+
[:header, [ [:attribute, :id], [:attribute, :user_id], [:attribute, :title]]]
|
146
|
+
]]
|
147
|
+
]]
|
148
|
+
]]
|
149
|
+
)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe '#method_missing' do
|
154
|
+
it 'proxies to the underlying relation' do
|
155
|
+
expect(users.gateway).to be(:default)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'returns proxy when response was not materialized' do
|
159
|
+
expect(users.by_pk(1)).to be_instance_of(ROM::Repository::RelationProxy)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'returns curried proxy when response was curried' do
|
163
|
+
expect(users.by_pk).to be_instance_of(ROM::Repository::RelationProxy)
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'raises when method is missing' do
|
167
|
+
expect { users.not_here }.to raise_error(NoMethodError, /not_here/)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -7,8 +7,8 @@ RSpec.describe 'SQL Relation extensions' do
|
|
7
7
|
it 'has valid column names' do
|
8
8
|
expect(users.attributes).to eql([:id, :name])
|
9
9
|
|
10
|
-
expect(users.
|
11
|
-
expect(users.
|
10
|
+
expect(users.by_pk.attributes).to eql([:name])
|
11
|
+
expect(users.by_pk(1).attributes).to eql([:name])
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -16,7 +16,7 @@ RSpec.describe 'SQL Relation extensions' do
|
|
16
16
|
context 'using short syntax' do
|
17
17
|
before do
|
18
18
|
configuration.relation(:users) do
|
19
|
-
view(:
|
19
|
+
view(:by_pk, [:name]) do |name|
|
20
20
|
where(name: name).select(:name)
|
21
21
|
end
|
22
22
|
end
|
@@ -28,7 +28,7 @@ RSpec.describe 'SQL Relation extensions' do
|
|
28
28
|
context 'with multi-block syntax' do
|
29
29
|
before do
|
30
30
|
configuration.relation(:users) do
|
31
|
-
view(:
|
31
|
+
view(:by_pk) do
|
32
32
|
header [:name]
|
33
33
|
|
34
34
|
relation do |name|
|
@@ -45,7 +45,7 @@ RSpec.describe 'SQL Relation extensions' do
|
|
45
45
|
it 'raises error' do
|
46
46
|
expect {
|
47
47
|
configuration.relation(:users) do
|
48
|
-
view(:
|
48
|
+
view(:by_pk) { |args| }
|
49
49
|
end
|
50
50
|
}.to raise_error(ArgumentError)
|
51
51
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
RSpec.describe 'struct builder', '#call' do
|
2
2
|
subject(:builder) { ROM::Repository::StructBuilder.new }
|
3
3
|
|
4
|
-
let(:input) { [:users, [:id, :name]] }
|
4
|
+
let(:input) { [:users, [:header, [[:attribute, :id], [:attribute, :name]]]] }
|
5
5
|
|
6
6
|
before { builder[*input] }
|
7
7
|
|
8
8
|
it 'generates a struct for a given relation name and columns' do
|
9
|
-
struct = builder.
|
9
|
+
struct = builder.class.cache[input.hash]
|
10
10
|
|
11
11
|
user = struct.new(id: 1, name: 'Jane')
|
12
12
|
|
@@ -17,9 +17,12 @@ RSpec.describe 'struct builder', '#call' do
|
|
17
17
|
expect(user[:name]).to eql('Jane')
|
18
18
|
|
19
19
|
expect(Hash[user]).to eql(id: 1, name: 'Jane')
|
20
|
+
|
21
|
+
expect(user.inspect).to eql('#<ROM::Struct[User] id=1 name="Jane">')
|
22
|
+
expect(user.to_s).to match(/\A#<ROM::Struct\[User\]:0x[0-9a-f]+>\z/)
|
20
23
|
end
|
21
24
|
|
22
|
-
it 'stores struct in the
|
23
|
-
expect(builder.
|
25
|
+
it 'stores struct in the cache' do
|
26
|
+
expect(builder.class.cache[input.hash]).to be(builder[*input])
|
24
27
|
end
|
25
28
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
RSpec.describe ROM::Struct do
|
2
|
+
subject(:struct) do
|
3
|
+
Class.new(ROM::Struct) do
|
4
|
+
attr_reader :id, :name
|
5
|
+
|
6
|
+
def initialize(id, name)
|
7
|
+
@id, @name = id, name
|
8
|
+
end
|
9
|
+
|
10
|
+
def id
|
11
|
+
@id.to_i
|
12
|
+
end
|
13
|
+
end.new("1", "Jane")
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#[]' do
|
17
|
+
it 'reads an attribute value' do
|
18
|
+
expect(struct.id).to be(1)
|
19
|
+
expect(struct.name).to eql("Jane")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
CHANGED
@@ -1,107 +1,88 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rom-repository
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: anima
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0.2'
|
20
|
-
- - ">="
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '0.2'
|
23
|
-
type: :runtime
|
24
|
-
prerelease: false
|
25
|
-
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
requirements:
|
27
|
-
- - "~>"
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '0.2'
|
30
|
-
- - ">="
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '0.2'
|
33
13
|
- !ruby/object:Gem::Dependency
|
34
14
|
name: rom
|
35
15
|
requirement: !ruby/object:Gem::Requirement
|
36
16
|
requirements:
|
37
17
|
- - "~>"
|
38
18
|
- !ruby/object:Gem::Version
|
39
|
-
version:
|
19
|
+
version: '2.0'
|
40
20
|
type: :runtime
|
41
21
|
prerelease: false
|
42
22
|
version_requirements: !ruby/object:Gem::Requirement
|
43
23
|
requirements:
|
44
24
|
- - "~>"
|
45
25
|
- !ruby/object:Gem::Version
|
46
|
-
version:
|
26
|
+
version: '2.0'
|
47
27
|
- !ruby/object:Gem::Dependency
|
48
28
|
name: rom-support
|
49
29
|
requirement: !ruby/object:Gem::Requirement
|
50
30
|
requirements:
|
51
31
|
- - "~>"
|
52
32
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
33
|
+
version: '2.0'
|
54
34
|
type: :runtime
|
55
35
|
prerelease: false
|
56
36
|
version_requirements: !ruby/object:Gem::Requirement
|
57
37
|
requirements:
|
58
38
|
- - "~>"
|
59
39
|
- !ruby/object:Gem::Version
|
60
|
-
version:
|
40
|
+
version: '2.0'
|
61
41
|
- !ruby/object:Gem::Dependency
|
62
42
|
name: rom-mapper
|
63
43
|
requirement: !ruby/object:Gem::Requirement
|
64
44
|
requirements:
|
65
45
|
- - "~>"
|
66
46
|
- !ruby/object:Gem::Version
|
67
|
-
version: 0.
|
47
|
+
version: '0.4'
|
68
48
|
type: :runtime
|
69
49
|
prerelease: false
|
70
50
|
version_requirements: !ruby/object:Gem::Requirement
|
71
51
|
requirements:
|
72
52
|
- - "~>"
|
73
53
|
- !ruby/object:Gem::Version
|
74
|
-
version: 0.
|
54
|
+
version: '0.4'
|
75
55
|
- !ruby/object:Gem::Dependency
|
76
56
|
name: rake
|
77
57
|
requirement: !ruby/object:Gem::Requirement
|
78
58
|
requirements:
|
79
59
|
- - "~>"
|
80
60
|
- !ruby/object:Gem::Version
|
81
|
-
version: '
|
61
|
+
version: '11.2'
|
82
62
|
type: :development
|
83
63
|
prerelease: false
|
84
64
|
version_requirements: !ruby/object:Gem::Requirement
|
85
65
|
requirements:
|
86
66
|
- - "~>"
|
87
67
|
- !ruby/object:Gem::Version
|
88
|
-
version: '
|
68
|
+
version: '11.2'
|
89
69
|
- !ruby/object:Gem::Dependency
|
90
70
|
name: rspec
|
91
71
|
requirement: !ruby/object:Gem::Requirement
|
92
72
|
requirements:
|
93
73
|
- - "~>"
|
94
74
|
- !ruby/object:Gem::Version
|
95
|
-
version: '3.
|
75
|
+
version: '3.5'
|
96
76
|
type: :development
|
97
77
|
prerelease: false
|
98
78
|
version_requirements: !ruby/object:Gem::Requirement
|
99
79
|
requirements:
|
100
80
|
- - "~>"
|
101
81
|
- !ruby/object:Gem::Version
|
102
|
-
version: '3.
|
103
|
-
description:
|
104
|
-
|
82
|
+
version: '3.5'
|
83
|
+
description: rom-repository adds support for auto-mapping and commands on top of rom-rb
|
84
|
+
relations
|
85
|
+
email: piotr.solnica+oss@gmail.com
|
105
86
|
executables: []
|
106
87
|
extensions: []
|
107
88
|
extra_rdoc_files: []
|
@@ -115,32 +96,48 @@ files:
|
|
115
96
|
- README.md
|
116
97
|
- Rakefile
|
117
98
|
- lib/rom-repository.rb
|
118
|
-
- lib/rom/plugins/relation/key_inference.rb
|
119
99
|
- lib/rom/repository.rb
|
100
|
+
- lib/rom/repository/changeset.rb
|
101
|
+
- lib/rom/repository/changeset/create.rb
|
102
|
+
- lib/rom/repository/changeset/pipe.rb
|
103
|
+
- lib/rom/repository/changeset/update.rb
|
104
|
+
- lib/rom/repository/class_interface.rb
|
105
|
+
- lib/rom/repository/command_compiler.rb
|
106
|
+
- lib/rom/repository/command_proxy.rb
|
120
107
|
- lib/rom/repository/header_builder.rb
|
121
|
-
- lib/rom/repository/loading_proxy.rb
|
122
|
-
- lib/rom/repository/loading_proxy/combine.rb
|
123
|
-
- lib/rom/repository/loading_proxy/wrap.rb
|
124
108
|
- lib/rom/repository/mapper_builder.rb
|
109
|
+
- lib/rom/repository/relation_proxy.rb
|
110
|
+
- lib/rom/repository/relation_proxy/wrap.rb
|
111
|
+
- lib/rom/repository/root.rb
|
112
|
+
- lib/rom/repository/struct_attributes.rb
|
125
113
|
- lib/rom/repository/struct_builder.rb
|
126
114
|
- lib/rom/repository/version.rb
|
127
115
|
- lib/rom/struct.rb
|
128
116
|
- log/.gitkeep
|
129
117
|
- rom-repository.gemspec
|
118
|
+
- spec/integration/changeset_spec.rb
|
119
|
+
- spec/integration/command_macros_spec.rb
|
120
|
+
- spec/integration/command_spec.rb
|
130
121
|
- spec/integration/multi_adapter_spec.rb
|
131
122
|
- spec/integration/repository_spec.rb
|
123
|
+
- spec/integration/root_repository_spec.rb
|
132
124
|
- spec/shared/database.rb
|
125
|
+
- spec/shared/mappers.rb
|
126
|
+
- spec/shared/models.rb
|
127
|
+
- spec/shared/plugins.rb
|
133
128
|
- spec/shared/relations.rb
|
134
129
|
- spec/shared/repo.rb
|
135
130
|
- spec/shared/seeds.rb
|
136
131
|
- spec/shared/structs.rb
|
137
132
|
- spec/spec_helper.rb
|
138
133
|
- spec/support/mapper_registry.rb
|
134
|
+
- spec/unit/changeset_spec.rb
|
139
135
|
- spec/unit/header_builder_spec.rb
|
140
|
-
- spec/unit/loading_proxy_spec.rb
|
141
136
|
- spec/unit/plugins/view_spec.rb
|
137
|
+
- spec/unit/relation_proxy_spec.rb
|
142
138
|
- spec/unit/sql/relation_spec.rb
|
143
139
|
- spec/unit/struct_builder_spec.rb
|
140
|
+
- spec/unit/struct_spec.rb
|
144
141
|
homepage: http://rom-rb.org
|
145
142
|
licenses:
|
146
143
|
- MIT
|
@@ -161,8 +158,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
161
158
|
version: '0'
|
162
159
|
requirements: []
|
163
160
|
rubyforge_project:
|
164
|
-
rubygems_version: 2.
|
161
|
+
rubygems_version: 2.5.1
|
165
162
|
signing_key:
|
166
163
|
specification_version: 4
|
167
|
-
summary: Repository for
|
164
|
+
summary: Repository abstraction for rom-rb
|
168
165
|
test_files: []
|
@@ -1,31 +0,0 @@
|
|
1
|
-
module ROM
|
2
|
-
module Plugins
|
3
|
-
module Relation
|
4
|
-
module KeyInference
|
5
|
-
# Infer foreign_key name for this relation
|
6
|
-
#
|
7
|
-
# TODO: this should be configurable and handled by an injected policy
|
8
|
-
#
|
9
|
-
# @return [Symbol]
|
10
|
-
#
|
11
|
-
# @api private
|
12
|
-
def foreign_key
|
13
|
-
:"#{Inflector.singularize(name)}_id"
|
14
|
-
end
|
15
|
-
|
16
|
-
# Return base name which defaults to name attribute
|
17
|
-
#
|
18
|
-
# @return [Symbol]
|
19
|
-
#
|
20
|
-
# @api private
|
21
|
-
def base_name
|
22
|
-
name
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
ROM.plugins do
|
30
|
-
register :key_inference, ROM::Plugins::Relation::KeyInference, type: :relation
|
31
|
-
end
|
@@ -1,158 +0,0 @@
|
|
1
|
-
module ROM
|
2
|
-
class Repository
|
3
|
-
class LoadingProxy
|
4
|
-
# Provides convenient methods for producing combined relations
|
5
|
-
#
|
6
|
-
# @api public
|
7
|
-
module Combine
|
8
|
-
# Combine with other relations
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# # combining many
|
12
|
-
# users.combine(many: { tasks: [tasks, id: :task_id] })
|
13
|
-
# users.combine(many: { tasks: [tasks.for_users, id: :task_id] })
|
14
|
-
#
|
15
|
-
# # combining one
|
16
|
-
# users.combine(one: { task: [tasks, id: :task_id] })
|
17
|
-
#
|
18
|
-
# @param [Hash] options
|
19
|
-
#
|
20
|
-
# @return [LoadingProxy]
|
21
|
-
#
|
22
|
-
# @api public
|
23
|
-
def combine(options)
|
24
|
-
combine_opts = options.each_with_object({}) do |(type, relations), result|
|
25
|
-
result[type] = relations.each_with_object({}) do |(name, (other, keys)), h|
|
26
|
-
h[name] = [
|
27
|
-
other.curried? ? other : other.combine_method(relation, keys), keys
|
28
|
-
]
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
nodes = combine_opts.flat_map do |type, relations|
|
33
|
-
relations.map { |name, (relation, keys)|
|
34
|
-
relation.combined(name, keys, type)
|
35
|
-
}
|
36
|
-
end
|
37
|
-
|
38
|
-
__new__(relation.combine(*nodes))
|
39
|
-
end
|
40
|
-
|
41
|
-
# Shortcut for combining with parents which infers the join keys
|
42
|
-
#
|
43
|
-
# @example
|
44
|
-
# tasks.combine_parents(one: users)
|
45
|
-
#
|
46
|
-
# @param [Hash] options
|
47
|
-
#
|
48
|
-
# @return [LoadingProxy]
|
49
|
-
#
|
50
|
-
# @api public
|
51
|
-
def combine_parents(options)
|
52
|
-
combine(options.each_with_object({}) { |(type, parents), h|
|
53
|
-
h[type] =
|
54
|
-
if parents.is_a?(Hash)
|
55
|
-
parents.each_with_object({}) { |(key, parent), r|
|
56
|
-
r[key] = [parent, combine_keys(parent, :parent)]
|
57
|
-
}
|
58
|
-
else
|
59
|
-
(parents.is_a?(Array) ? parents : [parents])
|
60
|
-
.each_with_object({}) { |parent, r|
|
61
|
-
r[parent.combine_tuple_key(type)] = [
|
62
|
-
parent, combine_keys(parent, :parent)
|
63
|
-
]
|
64
|
-
}
|
65
|
-
end
|
66
|
-
})
|
67
|
-
end
|
68
|
-
|
69
|
-
# Shortcut for combining with children which infers the join keys
|
70
|
-
#
|
71
|
-
# @example
|
72
|
-
# users.combine_parents(many: tasks)
|
73
|
-
#
|
74
|
-
# @param [Hash] options
|
75
|
-
#
|
76
|
-
# @return [LoadingProxy]
|
77
|
-
#
|
78
|
-
# @api public
|
79
|
-
def combine_children(options)
|
80
|
-
combine(options.each_with_object({}) { |(type, children), h|
|
81
|
-
h[type] =
|
82
|
-
if children.is_a?(Hash)
|
83
|
-
children.each_with_object({}) { |(key, child), r|
|
84
|
-
r[key] = [child, combine_keys(relation, :children)]
|
85
|
-
}
|
86
|
-
else
|
87
|
-
(children.is_a?(Array) ? children : [children])
|
88
|
-
.each_with_object({}) { |child, r|
|
89
|
-
r[child.combine_tuple_key(type)] = [
|
90
|
-
child, combine_keys(relation, :children)
|
91
|
-
]
|
92
|
-
}
|
93
|
-
end
|
94
|
-
})
|
95
|
-
end
|
96
|
-
|
97
|
-
# Infer join keys for a given relation and association type
|
98
|
-
#
|
99
|
-
# @param [LoadingProxy] relation
|
100
|
-
# @param [Symbol] type The type can be either :parent or :children
|
101
|
-
#
|
102
|
-
# @return [Hash<Symbol=>Symbol>]
|
103
|
-
#
|
104
|
-
# @api private
|
105
|
-
def combine_keys(relation, type)
|
106
|
-
if type == :parent
|
107
|
-
{ relation.foreign_key => relation.primary_key }
|
108
|
-
else
|
109
|
-
{ relation.primary_key => relation.foreign_key }
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# Infer relation for combine operation
|
114
|
-
#
|
115
|
-
# By default it uses `for_combine` which is implemented as SQL::Relation
|
116
|
-
# extension
|
117
|
-
#
|
118
|
-
# @return [LoadingProxy]
|
119
|
-
#
|
120
|
-
# @api private
|
121
|
-
def combine_method(other, keys)
|
122
|
-
custom_name = :"for_#{other.base_name}"
|
123
|
-
|
124
|
-
if relation.respond_to?(custom_name)
|
125
|
-
__send__(custom_name)
|
126
|
-
else
|
127
|
-
for_combine(keys)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
# Infer key under which a combine relation will be loaded
|
132
|
-
#
|
133
|
-
# @return [Symbol]
|
134
|
-
#
|
135
|
-
# @api private
|
136
|
-
def combine_tuple_key(arity)
|
137
|
-
if arity == :one
|
138
|
-
Inflector.singularize(base_name).to_sym
|
139
|
-
else
|
140
|
-
base_name
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
# Return combine representation of a loading-proxy relation
|
145
|
-
#
|
146
|
-
# This will carry meta info used to produce a correct AST from a relation
|
147
|
-
# so that correct mapper can be generated
|
148
|
-
#
|
149
|
-
# @return [LoadingProxy]
|
150
|
-
#
|
151
|
-
# @api private
|
152
|
-
def combined(name, keys, type)
|
153
|
-
with(name: name, meta: { keys: keys, combine_type: type })
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|