rom 3.0.0.beta2 → 3.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|