rom 0.6.0.beta3 → 0.6.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -2
- data/CHANGELOG.md +2 -1
- data/Gemfile +1 -1
- data/lib/rom.rb +0 -2
- data/lib/rom/commands/abstract.rb +1 -1
- data/lib/rom/env.rb +9 -7
- data/lib/rom/memory/storage.rb +6 -1
- data/lib/rom/relation.rb +14 -134
- data/lib/rom/relation/class_interface.rb +180 -0
- data/lib/rom/relation/lazy.rb +1 -1
- data/lib/rom/relation/registry_reader.rb +1 -1
- data/lib/rom/setup/finalize.rb +1 -21
- data/lib/rom/version.rb +1 -1
- data/rakelib/benchmark.rake +3 -1
- data/rom.gemspec +2 -2
- data/spec/integration/mappers/deep_embedded_spec.rb +1 -1
- data/spec/integration/mappers/definition_dsl_spec.rb +8 -6
- data/spec/integration/mappers/embedded_spec.rb +2 -6
- data/spec/integration/mappers/group_spec.rb +4 -4
- data/spec/integration/mappers/prefixing_attributes_spec.rb +1 -1
- data/spec/integration/mappers/renaming_attributes_spec.rb +5 -3
- data/spec/integration/mappers/symbolizing_attributes_spec.rb +2 -2
- data/spec/integration/mappers/wrap_spec.rb +4 -4
- data/spec/integration/multi_repo_spec.rb +5 -1
- data/spec/integration/relations/reading_spec.rb +6 -14
- data/spec/integration/setup_spec.rb +3 -3
- data/spec/unit/rom/env_spec.rb +1 -1
- data/spec/unit/rom/relation/lazy_spec.rb +16 -4
- data/spec/unit/rom/setup_spec.rb +2 -2
- metadata +9 -4
- data/lib/rom/reader.rb +0 -174
- data/spec/unit/rom/reader_spec.rb +0 -146
data/spec/unit/rom/setup_spec.rb
CHANGED
@@ -101,8 +101,8 @@ describe ROM::Setup do
|
|
101
101
|
expect(env.relations).to eql(ROM::RelationRegistry.new)
|
102
102
|
end
|
103
103
|
|
104
|
-
it 'builds empty
|
105
|
-
expect(env.
|
104
|
+
it 'builds empty mappers' do
|
105
|
+
expect(env.mappers).to eql(ROM::Registry.new)
|
106
106
|
end
|
107
107
|
|
108
108
|
it 'builds empty commands' do
|
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: 0.6.0.
|
4
|
+
version: 0.6.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: transproc
|
@@ -17,6 +17,9 @@ dependencies:
|
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0.1'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.1.2
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -24,6 +27,9 @@ dependencies:
|
|
24
27
|
- - "~>"
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: '0.1'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.1.2
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: equalizer
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -152,8 +158,8 @@ files:
|
|
152
158
|
- lib/rom/model_builder.rb
|
153
159
|
- lib/rom/processor.rb
|
154
160
|
- lib/rom/processor/transproc.rb
|
155
|
-
- lib/rom/reader.rb
|
156
161
|
- lib/rom/relation.rb
|
162
|
+
- lib/rom/relation/class_interface.rb
|
157
163
|
- lib/rom/relation/composite.rb
|
158
164
|
- lib/rom/relation/curried.rb
|
159
165
|
- lib/rom/relation/lazy.rb
|
@@ -220,7 +226,6 @@ files:
|
|
220
226
|
- spec/unit/rom/memory/storage_spec.rb
|
221
227
|
- spec/unit/rom/model_builder_spec.rb
|
222
228
|
- spec/unit/rom/processor/transproc_spec.rb
|
223
|
-
- spec/unit/rom/reader_spec.rb
|
224
229
|
- spec/unit/rom/registry_spec.rb
|
225
230
|
- spec/unit/rom/relation/composite_spec.rb
|
226
231
|
- spec/unit/rom/relation/lazy_spec.rb
|
data/lib/rom/reader.rb
DELETED
@@ -1,174 +0,0 @@
|
|
1
|
-
module ROM
|
2
|
-
# Exposes mapped tuples via enumerable interface
|
3
|
-
#
|
4
|
-
# See example for each method
|
5
|
-
#
|
6
|
-
# @api public
|
7
|
-
class Reader
|
8
|
-
include Enumerable
|
9
|
-
include Equalizer.new(:path, :relation, :mapper)
|
10
|
-
|
11
|
-
# Map relation to an array using a mapper
|
12
|
-
#
|
13
|
-
# @return [Array]
|
14
|
-
#
|
15
|
-
# @api public
|
16
|
-
alias_method :to_ary, :to_a
|
17
|
-
|
18
|
-
# @return [String] access path used to read a relation
|
19
|
-
#
|
20
|
-
# @api private
|
21
|
-
attr_reader :path
|
22
|
-
|
23
|
-
# @return [Relation] relation used by the reader
|
24
|
-
#
|
25
|
-
# @api private
|
26
|
-
attr_reader :relation
|
27
|
-
|
28
|
-
# @return [MapperRegistry] registry of mappers used by the reader
|
29
|
-
#
|
30
|
-
# @api private
|
31
|
-
attr_reader :mappers
|
32
|
-
|
33
|
-
# @return [Mapper] mapper to read the relation
|
34
|
-
#
|
35
|
-
# @api private
|
36
|
-
attr_reader :mapper
|
37
|
-
|
38
|
-
# Builds a reader instance for the provided relation
|
39
|
-
#
|
40
|
-
# @param [Symbol] name of the root relation
|
41
|
-
# @param [Relation] relation that the reader will use
|
42
|
-
# @param [MapperRegistry] mappers registry of mappers
|
43
|
-
# @param [Array<Symbol>] method_names a list of method names exposed by the relation
|
44
|
-
#
|
45
|
-
# @return [Reader]
|
46
|
-
#
|
47
|
-
# @api private
|
48
|
-
def self.build(name, relation, mappers, method_names = [])
|
49
|
-
klass = build_class(relation, method_names)
|
50
|
-
klass.new(name, relation, mappers)
|
51
|
-
end
|
52
|
-
|
53
|
-
# Build a reader subclass for the relation
|
54
|
-
#
|
55
|
-
# This method defines public methods on the class narrowing down data access
|
56
|
-
# only to the methods exposed by a given relation
|
57
|
-
#
|
58
|
-
# @param [Relation] relation that the reader will use
|
59
|
-
# @param [Array<Symbol>] method_names a list of method names exposed by the relation
|
60
|
-
#
|
61
|
-
# @return [Class]
|
62
|
-
#
|
63
|
-
# @api private
|
64
|
-
def self.build_class(relation, method_names)
|
65
|
-
klass_name = "#{Reader.name}[#{Inflector.camelize(relation.name)}]"
|
66
|
-
|
67
|
-
ClassBuilder.new(name: klass_name, parent: Reader).call do |klass|
|
68
|
-
method_names.each do |method_name|
|
69
|
-
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
70
|
-
def #{method_name}(*args, &block)
|
71
|
-
new_relation = relation.send(#{method_name.inspect}, *args, &block)
|
72
|
-
self.class.new(
|
73
|
-
new_path(#{method_name.to_s.inspect}), new_relation, mappers
|
74
|
-
)
|
75
|
-
end
|
76
|
-
RUBY
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# @api private
|
82
|
-
def initialize(path, relation, mappers, mapper = nil)
|
83
|
-
@path = path.to_s
|
84
|
-
@relation = relation
|
85
|
-
@mappers = mappers
|
86
|
-
@mapper = mapper || mappers.by_path(@path)
|
87
|
-
end
|
88
|
-
|
89
|
-
# @api private
|
90
|
-
def header
|
91
|
-
mapper.header
|
92
|
-
end
|
93
|
-
|
94
|
-
# Yields tuples mapped to objects
|
95
|
-
#
|
96
|
-
# @example
|
97
|
-
#
|
98
|
-
# # accessing root relation
|
99
|
-
# rom.read(:users).each { |user| # ... }
|
100
|
-
#
|
101
|
-
# # accessing virtual relations
|
102
|
-
# rom.read(:users).adults.recent.active.each { |user| # ... }
|
103
|
-
#
|
104
|
-
# @api public
|
105
|
-
def each
|
106
|
-
mapper.call(relation).each { |tuple| yield(tuple) }
|
107
|
-
end
|
108
|
-
|
109
|
-
# Returns a single tuple from the relation if there is one.
|
110
|
-
#
|
111
|
-
# @raise [ROM::TupleCountMismatchError] if the relation contains more than
|
112
|
-
# one tuple
|
113
|
-
#
|
114
|
-
# @api public
|
115
|
-
def one
|
116
|
-
if relation.count > 1
|
117
|
-
raise(
|
118
|
-
TupleCountMismatchError,
|
119
|
-
'The relation consists of more than one tuple'
|
120
|
-
)
|
121
|
-
else
|
122
|
-
mapper.call(relation).first
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
# Like [one], but additionally raises an error if the relation is empty.
|
127
|
-
#
|
128
|
-
# @raise [ROM::TupleCountMismatchError] if the relation does not contain
|
129
|
-
# exactly one tuple
|
130
|
-
#
|
131
|
-
# @api public
|
132
|
-
def one!
|
133
|
-
one || raise(
|
134
|
-
TupleCountMismatchError,
|
135
|
-
'The relation does not contain any tuples'
|
136
|
-
)
|
137
|
-
end
|
138
|
-
|
139
|
-
# Map tuples using a specific mapper if name is provided
|
140
|
-
#
|
141
|
-
# Defaults to Enumerable#map behavior
|
142
|
-
#
|
143
|
-
# @example
|
144
|
-
#
|
145
|
-
# rom.read(:users).map(:my_mapper_name)
|
146
|
-
# rom.read(:users).map { |user| ... }
|
147
|
-
#
|
148
|
-
# @return [Array,Reader]
|
149
|
-
#
|
150
|
-
# @api public
|
151
|
-
def map(*args)
|
152
|
-
if args.any?
|
153
|
-
mappers[args[0]].call(relation)
|
154
|
-
else
|
155
|
-
super
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
private
|
160
|
-
|
161
|
-
# @api private
|
162
|
-
def method_missing(name, *)
|
163
|
-
raise(
|
164
|
-
NoRelationError,
|
165
|
-
"undefined relation #{name.inspect} within #{path.inspect}"
|
166
|
-
)
|
167
|
-
end
|
168
|
-
|
169
|
-
# @api private
|
170
|
-
def new_path(name)
|
171
|
-
path.dup << ".#{name}"
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
@@ -1,146 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe ROM::Reader do
|
4
|
-
subject(:reader) { ROM::Reader.new(name, relation, mappers) }
|
5
|
-
|
6
|
-
let(:name) { :users }
|
7
|
-
let(:relation) { [jane, joe] }
|
8
|
-
let(:jane) { { name: 'Jane' } }
|
9
|
-
let(:joe) { { name: 'Joe' } }
|
10
|
-
let(:mappers) { ROM::MapperRegistry.new(users: mapper) }
|
11
|
-
let(:mapper) { double('mapper', header: []) }
|
12
|
-
|
13
|
-
describe '.build' do
|
14
|
-
subject(:reader) do
|
15
|
-
ROM::Reader.build(name, relation, mappers, [:all])
|
16
|
-
end
|
17
|
-
|
18
|
-
before do
|
19
|
-
relation.instance_exec do
|
20
|
-
def name
|
21
|
-
'users'
|
22
|
-
end
|
23
|
-
|
24
|
-
def all(*_args)
|
25
|
-
find_all
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'sets reader class name' do
|
31
|
-
expect(reader.class.name).to eql("ROM::Reader[Users]")
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'defines methods from relation' do
|
35
|
-
block = proc {}
|
36
|
-
|
37
|
-
user_id = 1
|
38
|
-
|
39
|
-
expect(relation).to receive(:all)
|
40
|
-
.with(user_id, &block)
|
41
|
-
.and_return([joe])
|
42
|
-
|
43
|
-
expect(mapper).to receive(:call)
|
44
|
-
.with([joe])
|
45
|
-
.and_return([joe])
|
46
|
-
|
47
|
-
result = reader.all(user_id, &block)
|
48
|
-
|
49
|
-
expect(result.path).to eql('users.all')
|
50
|
-
expect(result.to_a).to eql([joe])
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'raises error when relation does not respond to the method' do
|
54
|
-
expect { reader.not_here }
|
55
|
-
.to raise_error(ROM::NoRelationError, /not_here/)
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'raises error when relation does not respond to the method with args' do
|
59
|
-
expect { reader.find_by_id(1) }
|
60
|
-
.to raise_error(ROM::NoRelationError, /find_by_id/)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
describe '#initialize' do
|
65
|
-
it 'raises error when mapper cannot be found' do
|
66
|
-
expect { ROM::Reader.new(:not_here, relation, mappers) }
|
67
|
-
.to raise_error(ROM::MapperMissingError, /not_here/)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
describe '#each' do
|
72
|
-
it 'yields mapped tuples from relations' do
|
73
|
-
expect(mapper).to receive(:call)
|
74
|
-
.with(relation)
|
75
|
-
.and_return(relation)
|
76
|
-
|
77
|
-
result = []
|
78
|
-
reader.each { |user| result << user }
|
79
|
-
expect(result).to eql([jane, joe])
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
shared_examples_for 'one and one!' do |method|
|
84
|
-
context 'with a single tuple' do
|
85
|
-
let(:relation) { [jane] }
|
86
|
-
|
87
|
-
it 'returns a single tuple' do
|
88
|
-
expect(mapper).to receive(:call)
|
89
|
-
.with(relation)
|
90
|
-
.and_return(relation)
|
91
|
-
|
92
|
-
expect(reader.public_send(method)).to eql(jane)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
context 'with more than one tuple' do
|
97
|
-
it 'raises an error' do
|
98
|
-
expect { reader.public_send(method) }
|
99
|
-
.to raise_error(ROM::TupleCountMismatchError)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
describe '#one' do
|
105
|
-
it_should_behave_like 'one and one!', :one
|
106
|
-
|
107
|
-
context 'without any tuple' do
|
108
|
-
let(:relation) { [] }
|
109
|
-
|
110
|
-
it 'returns nil' do
|
111
|
-
expect(mapper).to receive(:call)
|
112
|
-
.with(relation)
|
113
|
-
.and_return(relation)
|
114
|
-
|
115
|
-
expect(reader.one).to be_nil
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
describe '#one!' do
|
121
|
-
it_should_behave_like 'one and one!', :one!
|
122
|
-
|
123
|
-
context 'without any tuple' do
|
124
|
-
let(:relation) { [] }
|
125
|
-
|
126
|
-
it 'raises an error' do
|
127
|
-
expect(mapper).to receive(:call)
|
128
|
-
.with(relation)
|
129
|
-
.and_return(relation)
|
130
|
-
|
131
|
-
expect { reader.one! }.to raise_error(ROM::TupleCountMismatchError)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
describe '#to_ary' do
|
137
|
-
it 'casts relation to an array with loaded objects' do
|
138
|
-
expect(mapper).to receive(:call)
|
139
|
-
.with(relation)
|
140
|
-
.and_return(relation)
|
141
|
-
|
142
|
-
result = reader.to_ary
|
143
|
-
expect(result).to eql(relation)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|