rom-repository 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -1
- data/lib/rom/repository/changeset/associated.rb +36 -12
- data/lib/rom/repository/changeset/restricted.rb +14 -0
- data/lib/rom/repository/changeset/stateful.rb +2 -2
- data/lib/rom/repository/changeset.rb +14 -0
- data/lib/rom/repository/version.rb +1 -1
- data/spec/shared/database.rb +10 -1
- data/spec/unit/changeset/associate_spec.rb +120 -0
- data/spec/unit/repository/changeset_spec.rb +45 -15
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 362832c67aba16f8f9a1dd37eac6d361b39e2344
|
4
|
+
data.tar.gz: bedb0cd2de862f654a709becc55095240a50dcb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2dfc1e26fd3ee71949cae67a87657b35e876588f930e00023c5fbf69a216882f35058b7f3c9038975ef619d972bc25b91f39bf8a7a69c3670923d649daf989fa
|
7
|
+
data.tar.gz: '02393346642d169103689807063b580ff765de616ffac3b15babdb757fef60bef91fe0bd27127bdb9720a07d296e0afdbf78a07b54b17fe75f62db9055ba8865'
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
# v1.3.0 2017-03-07
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* Support for chaining `Changeset#associate` (solnic)
|
6
|
+
* Inferring association name for `Changeset#associate` (solnic)
|
7
|
+
* Support for restricting `Update` and `Delete` changesets explicitly via `Changeset#by_pk` (solnic)
|
8
|
+
|
9
|
+
[Compare v1.2.0...v1.3.0](https://github.com/rom-rb/rom-repository/compare/v1.2.0...v1.3.0)
|
10
|
+
|
1
11
|
# v1.2.0 2017-03-01
|
2
12
|
|
3
13
|
### Fixed
|
@@ -7,7 +17,7 @@
|
|
7
17
|
|
8
18
|
### Changed
|
9
19
|
|
10
|
-
* Depends on rom >= 3.0
|
20
|
+
* Depends on rom >= 3.1.0 now as it provides `Relation::Graph#with_nodes` which is needed here (solnic)
|
11
21
|
|
12
22
|
[Compare v1.1.0...v1.2.0](https://github.com/rom-rb/rom-repository/compare/v1.1.0...v1.2.0)
|
13
23
|
|
@@ -12,13 +12,30 @@ module ROM
|
|
12
12
|
# @return [Changeset::Create] Child changeset
|
13
13
|
param :left
|
14
14
|
|
15
|
-
# @!attribute [r] right
|
16
|
-
# @return [Changeset::Create, Hash, #to_hash] Parent changeset or data
|
17
|
-
param :right
|
18
|
-
|
19
15
|
# @!attribute [r] association
|
20
16
|
# @return [Symbol] Association identifier from relation schema
|
21
|
-
option :
|
17
|
+
option :associations, reader: true
|
18
|
+
|
19
|
+
# Infer association name from an object with a schema
|
20
|
+
#
|
21
|
+
# This expects other to be an object with a schema that includes a primary key
|
22
|
+
# attribute with :source meta information. This makes it work with both structs
|
23
|
+
# and relations
|
24
|
+
#
|
25
|
+
# @see Stateful#associate
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
def self.infer_assoc_name(other)
|
29
|
+
schema = other.class.schema
|
30
|
+
attrs = schema.is_a?(Hash) ? schema.values : schema
|
31
|
+
pk = attrs.detect { |attr| attr.meta[:primary_key] }
|
32
|
+
|
33
|
+
if pk
|
34
|
+
pk.meta[:source].relation
|
35
|
+
else
|
36
|
+
raise ArgumentError, "can't infer association name for #{other}"
|
37
|
+
end
|
38
|
+
end
|
22
39
|
|
23
40
|
# Commit changeset's composite command
|
24
41
|
#
|
@@ -36,6 +53,11 @@ module ROM
|
|
36
53
|
command.call
|
37
54
|
end
|
38
55
|
|
56
|
+
# @api public
|
57
|
+
def associate(other, name = Associated.infer_assoc_name(other))
|
58
|
+
self.class.new(left, associations: associations.merge(name => other))
|
59
|
+
end
|
60
|
+
|
39
61
|
# Create a composed command
|
40
62
|
#
|
41
63
|
# @example using existing parent data
|
@@ -57,13 +79,15 @@ module ROM
|
|
57
79
|
#
|
58
80
|
# @api public
|
59
81
|
def command
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
82
|
+
associations.reduce(left.command.curry(left)) do |a, (assoc, other)|
|
83
|
+
case other
|
84
|
+
when Changeset
|
85
|
+
a >> other.command.with_association(assoc).curry(other)
|
86
|
+
when Associated
|
87
|
+
a >> other.command.with_association(assoc)
|
88
|
+
else
|
89
|
+
a.with_association(assoc, parent: other)
|
90
|
+
end
|
67
91
|
end
|
68
92
|
end
|
69
93
|
|
@@ -9,6 +9,20 @@ module ROM
|
|
9
9
|
def command
|
10
10
|
super.new(relation)
|
11
11
|
end
|
12
|
+
|
13
|
+
# Restrict changeset's relation by its PK
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# repo.changeset(UpdateUser).by_pk(1).data(name: "Jane")
|
17
|
+
#
|
18
|
+
# @param [Object] pk
|
19
|
+
#
|
20
|
+
# @return [Changeset]
|
21
|
+
#
|
22
|
+
# @api public
|
23
|
+
def by_pk(pk, data = EMPTY_HASH)
|
24
|
+
new(relation.by_pk(pk), __data__: data)
|
25
|
+
end
|
12
26
|
end
|
13
27
|
end
|
14
28
|
end
|
@@ -187,8 +187,8 @@ module ROM
|
|
187
187
|
# @param [Symbol] assoc The association identifier from schema
|
188
188
|
#
|
189
189
|
# @api public
|
190
|
-
def associate(other, name)
|
191
|
-
Associated.new(self,
|
190
|
+
def associate(other, name = Associated.infer_assoc_name(other))
|
191
|
+
Associated.new(self, associations: { name => other })
|
192
192
|
end
|
193
193
|
|
194
194
|
# Return command result type
|
@@ -92,6 +92,20 @@ module ROM
|
|
92
92
|
self.class.new(relation, options.merge(new_options))
|
93
93
|
end
|
94
94
|
|
95
|
+
# Return a new changeset with provided relation
|
96
|
+
#
|
97
|
+
# New options can be provided too
|
98
|
+
#
|
99
|
+
# @param [Relation] relation
|
100
|
+
# @param [Hash] options
|
101
|
+
#
|
102
|
+
# @return [Changeset]
|
103
|
+
#
|
104
|
+
# @api public
|
105
|
+
def new(relation, new_options = EMPTY_HASH)
|
106
|
+
self.class.new(relation, new_options.empty? ? options : options.merge(new_options))
|
107
|
+
end
|
108
|
+
|
95
109
|
# Persist changeset
|
96
110
|
#
|
97
111
|
# @example
|
data/spec/shared/database.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
-
RSpec.shared_context 'database' do
|
1
|
+
RSpec.shared_context 'database setup' do
|
2
2
|
let(:configuration) { ROM::Configuration.new(:sql, uri) }
|
3
|
+
|
3
4
|
let(:conn) { configuration.gateways[:default].connection }
|
5
|
+
|
4
6
|
let(:rom) { ROM.container(configuration) }
|
7
|
+
|
5
8
|
let(:uri) do
|
6
9
|
if defined? JRUBY_VERSION
|
7
10
|
'jdbc:postgresql://localhost/rom_repository'
|
@@ -12,7 +15,13 @@ RSpec.shared_context 'database' do
|
|
12
15
|
|
13
16
|
before do
|
14
17
|
conn.loggers << LOGGER
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
RSpec.shared_context 'database' do
|
22
|
+
include_context 'database setup'
|
15
23
|
|
24
|
+
before do
|
16
25
|
[:tags, :tasks, :posts, :users, :posts_labels, :labels, :books,
|
17
26
|
:reactions, :messages].each { |table| conn.drop_table?(table) }
|
18
27
|
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe ROM::Changeset, '#associate' do
|
4
|
+
include_context 'database setup'
|
5
|
+
|
6
|
+
before do
|
7
|
+
[:todos, :projects, :people].each { |table| conn.drop_table?(table) }
|
8
|
+
|
9
|
+
conn.create_table :people do
|
10
|
+
primary_key :id
|
11
|
+
column :name, String
|
12
|
+
end
|
13
|
+
|
14
|
+
conn.create_table :projects do
|
15
|
+
primary_key :id
|
16
|
+
column :name, String
|
17
|
+
end
|
18
|
+
|
19
|
+
conn.create_table :todos do
|
20
|
+
primary_key :id
|
21
|
+
foreign_key :user_id, :people, null: false, on_delete: :cascade
|
22
|
+
foreign_key :project_id, :projects, null: true, on_delete: :cascade
|
23
|
+
column :title, String
|
24
|
+
end
|
25
|
+
|
26
|
+
configuration.relation(:people) do
|
27
|
+
schema(infer: true) do
|
28
|
+
associations do
|
29
|
+
has_many :todos
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
configuration.relation(:projects) do
|
35
|
+
schema(infer: true) do
|
36
|
+
associations do
|
37
|
+
has_many :todos
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
configuration.relation(:todos) do
|
43
|
+
schema(infer: true) do
|
44
|
+
associations do
|
45
|
+
belongs_to :people, as: :user
|
46
|
+
belongs_to :projects, as: :project
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:user_repo) do
|
53
|
+
Class.new(ROM::Repository[:people]) { commands :create }.new(rom)
|
54
|
+
end
|
55
|
+
|
56
|
+
let(:project_repo) do
|
57
|
+
Class.new(ROM::Repository[:projects]) { commands :create }.new(rom)
|
58
|
+
end
|
59
|
+
|
60
|
+
let(:task_repo) do
|
61
|
+
Class.new(ROM::Repository[:todos]) { commands :create }.new(rom)
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'with Create' do
|
65
|
+
let(:jane) do
|
66
|
+
user_repo.create(name: 'Jane')
|
67
|
+
end
|
68
|
+
|
69
|
+
let(:project) do
|
70
|
+
project_repo.create(name: 'rom-rb')
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'associates child with parent' do
|
74
|
+
changeset = task_repo.changeset(title: 'Test 1').associate(jane, :user)
|
75
|
+
|
76
|
+
expect(changeset.commit).to include(user_id: jane.id, title: 'Test 1')
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'associates child with multiple parents' do
|
80
|
+
changeset = task_repo.changeset(title: 'Test 1').
|
81
|
+
associate(jane, :user).
|
82
|
+
associate(project)
|
83
|
+
|
84
|
+
expect(changeset.commit).
|
85
|
+
to include(user_id: jane.id, project_id: project.id, title: 'Test 1')
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'raises when assoc name cannot be inferred' do
|
89
|
+
other = Class.new do
|
90
|
+
def self.schema
|
91
|
+
[]
|
92
|
+
end
|
93
|
+
end.new
|
94
|
+
|
95
|
+
expect { task_repo.changeset(title: 'Test 1').associate(other) }.
|
96
|
+
to raise_error(ArgumentError, /can't infer association name for/)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'with Update' do
|
101
|
+
let!(:john) do
|
102
|
+
user_repo.create(name: 'John')
|
103
|
+
end
|
104
|
+
|
105
|
+
let!(:jane) do
|
106
|
+
user_repo.create(name: 'Jane')
|
107
|
+
end
|
108
|
+
|
109
|
+
let!(:task) do
|
110
|
+
task_repo.create(title: 'Test 1', user_id: john.id)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'associates child with parent' do
|
114
|
+
changeset = task_repo.changeset(task.id, title: 'Test 2')
|
115
|
+
|
116
|
+
expect(changeset.associate(jane, :user).commit).
|
117
|
+
to include(user_id: jane.id, title: 'Test 2')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -121,28 +121,58 @@ RSpec.describe ROM::Repository, '#changeset' do
|
|
121
121
|
end
|
122
122
|
|
123
123
|
describe 'custom changeset class' do
|
124
|
-
|
125
|
-
|
126
|
-
|
124
|
+
context 'with a Create' do
|
125
|
+
let(:changeset) do
|
126
|
+
repo.changeset(changeset_class[:users]).data({})
|
127
|
+
end
|
127
128
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
129
|
+
let(:changeset_class) do
|
130
|
+
Class.new(ROM::Changeset::Create) do
|
131
|
+
def to_h
|
132
|
+
__data__.merge(name: 'Jane')
|
133
|
+
end
|
132
134
|
end
|
133
135
|
end
|
134
|
-
end
|
135
136
|
|
136
|
-
|
137
|
-
|
138
|
-
|
137
|
+
it 'has data' do
|
138
|
+
expect(changeset.to_h).to eql(name: 'Jane')
|
139
|
+
end
|
139
140
|
|
140
|
-
|
141
|
-
|
141
|
+
it 'has relation' do
|
142
|
+
expect(changeset.relation).to be(repo.users)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'can be commited' do
|
146
|
+
expect(changeset.commit).to eql(id: 1, name: 'Jane')
|
147
|
+
end
|
142
148
|
end
|
143
149
|
|
144
|
-
|
145
|
-
|
150
|
+
context 'with an Update' do
|
151
|
+
let(:changeset) do
|
152
|
+
repo.changeset(changeset_class).by_pk(user.id, name: 'Jade')
|
153
|
+
end
|
154
|
+
|
155
|
+
let(:changeset_class) do
|
156
|
+
Class.new(ROM::Changeset::Update[:users]) do
|
157
|
+
map { |t| t.merge(name: "#{t[:name]} Doe") }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
let(:user) do
|
162
|
+
repo.command(:create, repo.users).call(name: 'Jane')
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'has data' do
|
166
|
+
expect(changeset.to_h).to eql(name: 'Jade Doe')
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'has relation restricted by pk' do
|
170
|
+
expect(changeset.relation.dataset).to eql(repo.users.by_pk(user.id).dataset)
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'can be commited' do
|
174
|
+
expect(changeset.commit).to eql(id: 1, name: 'Jade Doe')
|
175
|
+
end
|
146
176
|
end
|
147
177
|
end
|
148
178
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rom-repository
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.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: 2017-03-
|
11
|
+
date: 2017-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rom
|
@@ -161,6 +161,7 @@ files:
|
|
161
161
|
- spec/spec_helper.rb
|
162
162
|
- spec/support/mapper_registry.rb
|
163
163
|
- spec/support/mutant.rb
|
164
|
+
- spec/unit/changeset/associate_spec.rb
|
164
165
|
- spec/unit/changeset/map_spec.rb
|
165
166
|
- spec/unit/changeset_spec.rb
|
166
167
|
- spec/unit/relation_proxy_spec.rb
|