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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1d1abbcf0ab779840aaf19b899c6e5b725c8621e
4
- data.tar.gz: b4cfd46194e94c7509c920c21f54e4d317ace881
3
+ metadata.gz: 362832c67aba16f8f9a1dd37eac6d361b39e2344
4
+ data.tar.gz: bedb0cd2de862f654a709becc55095240a50dcb2
5
5
  SHA512:
6
- metadata.gz: d96d070cf7eaf2729c8ebd1b78ed8cc9aebfc128d01c81211eca941fd53ad3f84d6be86ab2ba62e6ef9d1cbf97d4c1eaf299906bb7f0aac26842c070ac9f8c69
7
- data.tar.gz: b82756a5892e7d62c5ce822372f51bd2b8af2e76d28b0e8dde4b01a9e798460bafac9b23ce511fbe761350c598b240024a0f6203e4946f784cb595d0df917c0d
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.4 now as it provides `Relation::Graph#with_nodes` which is needed here (solnic)
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 :association, reader: true
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
- case right
61
- when Changeset
62
- left.command.curry(left) >> right.command.with_association(association).curry(right)
63
- when Associated
64
- left.command.curry(left) >> right.command.with_association(association)
65
- else
66
- left.command.with_association(association).curry(left, right)
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, other, association: name)
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
@@ -1,5 +1,5 @@
1
1
  module ROM
2
2
  class Repository
3
- VERSION = '1.2.0'.freeze
3
+ VERSION = '1.3.0'.freeze
4
4
  end
5
5
  end
@@ -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
- let(:changeset) do
125
- repo.changeset(changeset_class[:users]).data({})
126
- end
124
+ context 'with a Create' do
125
+ let(:changeset) do
126
+ repo.changeset(changeset_class[:users]).data({})
127
+ end
127
128
 
128
- let(:changeset_class) do
129
- Class.new(ROM::Changeset::Create) do
130
- def to_h
131
- __data__.merge(name: 'Jane')
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
- it 'has data' do
137
- expect(changeset.to_h).to eql(name: 'Jane')
138
- end
137
+ it 'has data' do
138
+ expect(changeset.to_h).to eql(name: 'Jane')
139
+ end
139
140
 
140
- it 'has relation' do
141
- expect(changeset.relation).to be(repo.users)
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
- it 'can be commited' do
145
- expect(changeset.commit).to eql(id: 1, name: 'Jane')
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.2.0
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-01 00:00:00.000000000 Z
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