rom-repository 1.2.0 → 1.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/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
|