rom 3.0.0.beta2 → 3.0.0.beta3
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 +2 -1
- data/lib/rom/command.rb +7 -2
- data/lib/rom/plugins/command/schema.rb +2 -2
- data/lib/rom/relation.rb +14 -5
- data/lib/rom/schema.rb +41 -1
- data/lib/rom/schema/dsl.rb +8 -2
- data/lib/rom/schema/type.rb +14 -0
- data/lib/rom/version.rb +1 -1
- data/spec/unit/rom/commands/pre_and_post_processors_spec.rb +12 -2
- data/spec/unit/rom/commands_spec.rb +17 -0
- data/spec/unit/rom/plugins/command/schema_spec.rb +5 -5
- data/spec/unit/rom/relation/call_spec.rb +51 -0
- data/spec/unit/rom/relation/schema_spec.rb +20 -0
- data/spec/unit/rom/relation_spec.rb +3 -3
- data/spec/unit/rom/schema/append_spec.rb +17 -0
- data/spec/unit/rom/schema/uniq_spec.rb +21 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98cf29125e7e877f9eadf9603755d7bacf9603af
|
4
|
+
data.tar.gz: 596ae65060eb04d7f52be2406908a4541b758c6e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8cf082f7aa10bc96e0b186f8bf42b6d64fa3a703780cf763292f2393374009a5a26f5387b4b0df2381371d82cf49bdb210438c07fb0b9303a72d5065e3952fd8
|
7
|
+
data.tar.gz: 864cd037917d3df2ded0efca14a7a27aa53023ce296532df70aa28f7cb5ad1b917b41b6ada18e6be6ec566f321cd6d4920d9f954096330e07e11ab89539f40b4
|
data/CHANGELOG.md
CHANGED
@@ -6,13 +6,14 @@
|
|
6
6
|
* Schemas have their own types with adapter-specific APIs (solnic)
|
7
7
|
* Schema attributes include meta properties `source` and `target` (for FKs) (solnic)
|
8
8
|
* Inferred schemas can have explicit attribute definitions for cases where inference didn't work (solnic)
|
9
|
-
* New schema APIs: `#project`, `#rename`, `#exclude`, `#prefix`, `#wrap` and `#
|
9
|
+
* New schema APIs: `#project`, `#rename`, `#exclude`, `#prefix`, `#wrap`, `#merge`, `#append` and `#uniq` (solnic)
|
10
10
|
* New schema attribute APIs: `#name`, `#aliased`, `#aliased?`, `#prefixed`, `#prefixed?`, `#wrapped`, `#wrapped?`, `#source`, `#target`, `#primary_key?`, `#foreign_key?` (solnic)
|
11
11
|
* Schemas are coercible to arrays that include all attribute types (solnic)
|
12
12
|
* Automatic relation view projection via `Schema#call` (abstract method for adapters) (solnic)
|
13
13
|
* `Relation#new(dataset, new_opts)` interface (solnic)
|
14
14
|
* `Relation#[]` interface for convenient access to schema attributes (solnic)
|
15
15
|
* `Command` has now support for `before` and `after` hooks (solnic)
|
16
|
+
* Support for `read` types in schemas, these are used when relation loads its tuples (solnic)
|
16
17
|
* New `Command#before` method for creating a command with before hook(s) at run-time (solnic)
|
17
18
|
* New `Command#after` method for creating a command with after hook(s) at run-time (solnic)
|
18
19
|
* New `Gateway#transaction` method runs code inside a transaction (flash-gordon)
|
data/lib/rom/command.rb
CHANGED
@@ -170,14 +170,19 @@ module ROM
|
|
170
170
|
self.class.build(new_relation, options.merge(source: relation))
|
171
171
|
end
|
172
172
|
|
173
|
+
# @api public
|
174
|
+
def with_opts(new_opts)
|
175
|
+
self.class.new(relation, options.merge(new_opts))
|
176
|
+
end
|
177
|
+
|
173
178
|
# @api public
|
174
179
|
def before(*hooks)
|
175
|
-
self.class.new(relation, options.merge(before: hooks))
|
180
|
+
self.class.new(relation, options.merge(before: before_hooks + hooks))
|
176
181
|
end
|
177
182
|
|
178
183
|
# @api public
|
179
184
|
def after(*hooks)
|
180
|
-
self.class.new(relation, options.merge(after: hooks))
|
185
|
+
self.class.new(relation, options.merge(after: after_hooks + hooks))
|
181
186
|
end
|
182
187
|
|
183
188
|
private
|
@@ -20,9 +20,9 @@ module ROM
|
|
20
20
|
|
21
21
|
input_handler =
|
22
22
|
if default_input != Hash && relation.schema?
|
23
|
-
-> tuple { relation.
|
23
|
+
-> tuple { relation.input_schema[input[tuple]] }
|
24
24
|
elsif relation.schema?
|
25
|
-
relation.
|
25
|
+
relation.input_schema
|
26
26
|
else
|
27
27
|
default_input
|
28
28
|
end
|
data/lib/rom/relation.rb
CHANGED
@@ -30,6 +30,8 @@ module ROM
|
|
30
30
|
#
|
31
31
|
# @api public
|
32
32
|
class Relation
|
33
|
+
NOOP_OUTPUT_SCHEMA = -> tuple { tuple }.freeze
|
34
|
+
|
33
35
|
extend Initializer
|
34
36
|
extend ClassInterface
|
35
37
|
|
@@ -51,13 +53,20 @@ module ROM
|
|
51
53
|
# schema (if it was defined) and sets an empty one as
|
52
54
|
# the fallback
|
53
55
|
# @api public
|
54
|
-
option :schema, reader: true, default: method(:default_schema).to_proc
|
56
|
+
option :schema, reader: true, optional: true, default: method(:default_schema).to_proc
|
55
57
|
|
56
|
-
# @!attribute [r]
|
58
|
+
# @!attribute [r] input_schema
|
57
59
|
# @return [Object#[]] tuple processing function, uses schema or defaults to Hash[]
|
58
60
|
# @api private
|
59
|
-
option :
|
60
|
-
relation.schema? ?
|
61
|
+
option :input_schema, reader: true, default: -> relation {
|
62
|
+
relation.schema? ? schema.to_input_hash : Hash
|
63
|
+
}
|
64
|
+
|
65
|
+
# @!attribute [r] output_schema
|
66
|
+
# @return [Object#[]] tuple processing function, uses schema or defaults to NOOP_OUTPUT_SCHEMA
|
67
|
+
# @api private
|
68
|
+
option :output_schema, reader: true, optional: true, default: -> relation {
|
69
|
+
relation.schema.any?(&:read?) ? schema.to_output_hash : NOOP_OUTPUT_SCHEMA
|
61
70
|
}
|
62
71
|
|
63
72
|
# Return schema attribute
|
@@ -77,7 +86,7 @@ module ROM
|
|
77
86
|
# @api public
|
78
87
|
def each(&block)
|
79
88
|
return to_enum unless block
|
80
|
-
dataset.each { |tuple| yield(tuple) }
|
89
|
+
dataset.each { |tuple| yield(output_schema[tuple]) }
|
81
90
|
end
|
82
91
|
|
83
92
|
# Composes with other relations
|
data/lib/rom/schema.rb
CHANGED
@@ -205,10 +205,36 @@ module ROM
|
|
205
205
|
#
|
206
206
|
# @api public
|
207
207
|
def merge(other)
|
208
|
-
|
208
|
+
append(*other)
|
209
209
|
end
|
210
210
|
alias_method :+, :merge
|
211
211
|
|
212
|
+
# Append more attributes to the schema
|
213
|
+
#
|
214
|
+
# @param [*Array<Schema::Type>]
|
215
|
+
#
|
216
|
+
# @return [Schema]
|
217
|
+
#
|
218
|
+
# @api public
|
219
|
+
def append(*new_attributes)
|
220
|
+
new(attributes + new_attributes)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Return a new schema with uniq attributes
|
224
|
+
#
|
225
|
+
# @param [*Array<Schema::Type>]
|
226
|
+
#
|
227
|
+
# @return [Schema]
|
228
|
+
#
|
229
|
+
# @api public
|
230
|
+
def uniq(&block)
|
231
|
+
if block
|
232
|
+
new(attributes.uniq(&block))
|
233
|
+
else
|
234
|
+
new(attributes.uniq(&:name))
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
212
238
|
# Return if a schema includes an attribute with the given name
|
213
239
|
#
|
214
240
|
# @param [Symbol] name The name of the attribute
|
@@ -256,6 +282,20 @@ module ROM
|
|
256
282
|
freeze
|
257
283
|
end
|
258
284
|
|
285
|
+
# @api private
|
286
|
+
def to_output_hash
|
287
|
+
Types::Coercible::Hash.schema(
|
288
|
+
map { |attr| [attr.name, attr.to_read_type] }.to_h
|
289
|
+
)
|
290
|
+
end
|
291
|
+
|
292
|
+
# @api private
|
293
|
+
def to_input_hash
|
294
|
+
Types::Coercible::Hash.schema(
|
295
|
+
map { |attr| [attr.name, attr] }.to_h
|
296
|
+
)
|
297
|
+
end
|
298
|
+
|
259
299
|
private
|
260
300
|
|
261
301
|
# @api private
|
data/lib/rom/schema/dsl.rb
CHANGED
@@ -32,9 +32,15 @@ module ROM
|
|
32
32
|
# @see Relation.schema
|
33
33
|
#
|
34
34
|
# @api public
|
35
|
-
def attribute(name, type)
|
35
|
+
def attribute(name, type, options = EMPTY_HASH)
|
36
36
|
@attributes ||= {}
|
37
|
-
|
37
|
+
|
38
|
+
@attributes[name] =
|
39
|
+
if options[:read]
|
40
|
+
type.meta(name: name, source: relation, read: options[:read])
|
41
|
+
else
|
42
|
+
type.meta(name: name, source: relation)
|
43
|
+
end
|
38
44
|
end
|
39
45
|
|
40
46
|
# Specify which key(s) should be the primary key
|
data/lib/rom/schema/type.rb
CHANGED
@@ -13,6 +13,20 @@ module ROM
|
|
13
13
|
@type = type
|
14
14
|
end
|
15
15
|
|
16
|
+
# @api private
|
17
|
+
def [](input)
|
18
|
+
type[input]
|
19
|
+
end
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
def read?
|
23
|
+
! meta[:read].nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_read_type
|
27
|
+
read? ? meta[:read] : type
|
28
|
+
end
|
29
|
+
|
16
30
|
# @api public
|
17
31
|
def primary_key?
|
18
32
|
meta[:primary_key].equal?(true)
|
data/lib/rom/version.rb
CHANGED
@@ -8,26 +8,36 @@ RSpec.describe ROM::Command, 'before/after hooks' do
|
|
8
8
|
describe '#before' do
|
9
9
|
subject(:command) do
|
10
10
|
Class.new(ROM::Command) do
|
11
|
+
before :init
|
12
|
+
|
13
|
+
def init(*)
|
14
|
+
end
|
15
|
+
|
11
16
|
def prepare(*)
|
12
17
|
end
|
13
18
|
end.build(relation)
|
14
19
|
end
|
15
20
|
|
16
21
|
it 'returns a new command with configured before hooks' do
|
17
|
-
expect(command.before(:prepare).before_hooks).to
|
22
|
+
expect(command.before(:prepare).before_hooks).to eql(%i[init prepare])
|
18
23
|
end
|
19
24
|
end
|
20
25
|
|
21
26
|
describe '#after' do
|
22
27
|
subject(:command) do
|
23
28
|
Class.new(ROM::Command) do
|
29
|
+
after :finalize
|
30
|
+
|
31
|
+
def finalize(*)
|
32
|
+
end
|
33
|
+
|
24
34
|
def prepare(*)
|
25
35
|
end
|
26
36
|
end.build(relation)
|
27
37
|
end
|
28
38
|
|
29
39
|
it 'returns a new command with configured after hooks' do
|
30
|
-
expect(command.after(:prepare).after_hooks).to
|
40
|
+
expect(command.after(:prepare).after_hooks).to eql(%i[finalize prepare])
|
31
41
|
end
|
32
42
|
end
|
33
43
|
|
@@ -168,4 +168,21 @@ RSpec.describe 'Commands' do
|
|
168
168
|
expect(command.call('foo')).to be(ROM::EMPTY_ARRAY)
|
169
169
|
end
|
170
170
|
end
|
171
|
+
|
172
|
+
describe '#with_opts' do
|
173
|
+
subject(:command) do
|
174
|
+
Class.new(ROM::Command::Create).build(relation, options)
|
175
|
+
end
|
176
|
+
|
177
|
+
let(:relation) { double(:relation) }
|
178
|
+
let(:options) { { result: :one } }
|
179
|
+
|
180
|
+
it 'returns a new command with updated options' do
|
181
|
+
new_command = command.with_opts(before: :test)
|
182
|
+
|
183
|
+
expect(new_command.relation).to be(relation)
|
184
|
+
expect(new_command.result).to be(:one)
|
185
|
+
expect(new_command.before_hooks).to eql([:test])
|
186
|
+
end
|
187
|
+
end
|
171
188
|
end
|
@@ -38,17 +38,17 @@ RSpec.describe ROM::Plugins::Command::Schema do
|
|
38
38
|
|
39
39
|
context 'when relation has a schema' do
|
40
40
|
let(:relation) do
|
41
|
-
instance_double(ROM::Relation, schema?: true,
|
41
|
+
instance_double(ROM::Relation, schema?: true, input_schema: input_schema)
|
42
42
|
end
|
43
43
|
|
44
|
-
let(:
|
45
|
-
double(:
|
44
|
+
let(:input_schema) do
|
45
|
+
double(:input_schema)
|
46
46
|
end
|
47
47
|
|
48
48
|
it 'sets schema hash as input handler' do
|
49
49
|
command = Class.new(command_class).build(relation)
|
50
50
|
|
51
|
-
expect(command.input).to be(
|
51
|
+
expect(command.input).to be(input_schema)
|
52
52
|
end
|
53
53
|
|
54
54
|
it 'sets a composed input handler with schema hash and a custom one' do
|
@@ -57,7 +57,7 @@ RSpec.describe ROM::Plugins::Command::Schema do
|
|
57
57
|
command = Class.new(command_class) { input my_handler }.build(relation)
|
58
58
|
|
59
59
|
expect(my_handler).to receive(:[]).with('some value').and_return('my handler')
|
60
|
-
expect(
|
60
|
+
expect(input_schema).to receive(:[]).with('my handler').and_return('a tuple')
|
61
61
|
|
62
62
|
expect(command.input['some value']).to eql('a tuple')
|
63
63
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rom/relation'
|
2
|
+
|
3
|
+
RSpec.describe ROM::Relation, '#call' do
|
4
|
+
subject(:relation) do
|
5
|
+
relation_class.new(data)
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'without read types in schema' do
|
9
|
+
let(:relation_class) do
|
10
|
+
Class.new(ROM::Relation[:memory]) do
|
11
|
+
schema do
|
12
|
+
attribute :id, ROM::Types::Int
|
13
|
+
attribute :name, ROM::Types::String
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:data) do
|
19
|
+
[{ id: '1', name: 'Jane' }, { id: '2', name: 'John'} ]
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'has noop output_schema' do
|
23
|
+
expect(relation.output_schema).to be(ROM::Relation::NOOP_OUTPUT_SCHEMA)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns loaded relation with data' do
|
27
|
+
expect(relation.call.collection).
|
28
|
+
to eql(data)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'with read types in schema' do
|
33
|
+
let(:relation_class) do
|
34
|
+
Class.new(ROM::Relation[:memory]) do
|
35
|
+
schema do
|
36
|
+
attribute :id, ROM::Types::String, read: ROM::Types::Coercible::Int
|
37
|
+
attribute :name, ROM::Types::String
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:data) do
|
43
|
+
[{ id: '1', name: 'Jane' }, { id: '2', name: 'John'} ]
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns loaded relation with coerced data' do
|
47
|
+
expect(relation.call.collection).
|
48
|
+
to eql([{ id: 1, name: 'Jane' }, { id: 2, name: 'John'} ])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -29,6 +29,26 @@ RSpec.describe ROM::Relation, '.schema' do
|
|
29
29
|
expect(Test::Users.schema).to eql(schema)
|
30
30
|
end
|
31
31
|
|
32
|
+
it 'allows defining types for reading tuples' do
|
33
|
+
module Test
|
34
|
+
module Types
|
35
|
+
CoercibleDate = ROM::Types::Date.constructor(Date.method(:parse))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Test::Users < ROM::Relation[:memory]
|
40
|
+
schema do
|
41
|
+
attribute :id, Types::Int
|
42
|
+
attribute :date, Types::Coercible::String, read: Test::Types::CoercibleDate
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
schema = Test::Users.schema
|
47
|
+
|
48
|
+
expect(schema.to_output_hash).
|
49
|
+
to eql(ROM::Types::Coercible::Hash.schema(id: schema[:id].type, date: schema[:date].meta[:read]))
|
50
|
+
end
|
51
|
+
|
32
52
|
it 'allows setting composite primary key' do
|
33
53
|
class Test::Users < ROM::Relation[:memory]
|
34
54
|
schema do
|
@@ -232,13 +232,13 @@ RSpec.describe ROM::Relation do
|
|
232
232
|
end
|
233
233
|
end
|
234
234
|
|
235
|
-
describe '#
|
235
|
+
describe '#input_schema' do
|
236
236
|
it 'returns a schema hash type' do
|
237
237
|
relation = Class.new(ROM::Relation[:memory]) do
|
238
238
|
schema { attribute :id, ROM::Types::Coercible::Int }
|
239
239
|
end.new([])
|
240
240
|
|
241
|
-
expect(relation.
|
241
|
+
expect(relation.input_schema[id: '1']).to eql(id: 1)
|
242
242
|
end
|
243
243
|
|
244
244
|
it 'returns a plain Hash coercer when there is no schema' do
|
@@ -246,7 +246,7 @@ RSpec.describe ROM::Relation do
|
|
246
246
|
|
247
247
|
tuple = [[:id, '1']]
|
248
248
|
|
249
|
-
expect(relation.
|
249
|
+
expect(relation.input_schema[tuple]).to eql(id: '1')
|
250
250
|
end
|
251
251
|
end
|
252
252
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rom/schema'
|
2
|
+
|
3
|
+
RSpec.describe ROM::Schema, '#append' do
|
4
|
+
subject(:schema) { left.append(*right) }
|
5
|
+
|
6
|
+
let(:left) do
|
7
|
+
define_schema(:users, id: :Int, name: :String)
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:right) do
|
11
|
+
define_schema(:tasks, user_id: :Int)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns a new schema with attributes from two schemas' do
|
15
|
+
expect(schema.map(&:name)).to eql(%i[id name user_id])
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rom/schema'
|
2
|
+
|
3
|
+
RSpec.describe ROM::Schema, '#uniq' do
|
4
|
+
subject(:schema) { left.merge(right) }
|
5
|
+
|
6
|
+
let(:left) do
|
7
|
+
define_schema(:users, id: :Int, name: :String)
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:right) do
|
11
|
+
define_schema(:tasks, id: :Int, user_id: :Int)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns a new schema with unique attributes from two schemas' do
|
15
|
+
expect(schema.uniq.map(&:name)).to eql(%i[id name user_id])
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'accepts a block' do
|
19
|
+
expect(schema.uniq(&:primitive).map(&:name)).to eql(%i[id name])
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0.
|
4
|
+
version: 3.0.0.beta3
|
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-01-
|
11
|
+
date: 2017-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -345,6 +345,7 @@ files:
|
|
345
345
|
- spec/unit/rom/plugins/relation/key_inference_spec.rb
|
346
346
|
- spec/unit/rom/registry_spec.rb
|
347
347
|
- spec/unit/rom/relation/attribute_reader_spec.rb
|
348
|
+
- spec/unit/rom/relation/call_spec.rb
|
348
349
|
- spec/unit/rom/relation/composite_spec.rb
|
349
350
|
- spec/unit/rom/relation/curried_spec.rb
|
350
351
|
- spec/unit/rom/relation/graph_spec.rb
|
@@ -356,6 +357,7 @@ files:
|
|
356
357
|
- spec/unit/rom/relation/view_spec.rb
|
357
358
|
- spec/unit/rom/relation_spec.rb
|
358
359
|
- spec/unit/rom/schema/accessing_attributes_spec.rb
|
360
|
+
- spec/unit/rom/schema/append_spec.rb
|
359
361
|
- spec/unit/rom/schema/exclude_spec.rb
|
360
362
|
- spec/unit/rom/schema/finalize_spec.rb
|
361
363
|
- spec/unit/rom/schema/key_predicate_spec.rb
|
@@ -364,6 +366,7 @@ files:
|
|
364
366
|
- spec/unit/rom/schema/project_spec.rb
|
365
367
|
- spec/unit/rom/schema/rename_spec.rb
|
366
368
|
- spec/unit/rom/schema/type_spec.rb
|
369
|
+
- spec/unit/rom/schema/uniq_spec.rb
|
367
370
|
- spec/unit/rom/schema/wrap_spec.rb
|
368
371
|
- spec/unit/rom/schema_spec.rb
|
369
372
|
- spec/unit/rom/setup/auto_registration_spec.rb
|