rom-mapper 0.1.1 → 0.2.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 +7 -0
- data/.gitignore +2 -0
- data/.rspec +1 -1
- data/.travis.yml +19 -13
- data/{Changelog.md → CHANGELOG.md} +6 -0
- data/Gemfile +23 -10
- data/README.md +17 -12
- data/Rakefile +12 -4
- data/lib/rom-mapper.rb +6 -15
- data/lib/rom/header.rb +195 -0
- data/lib/rom/header/attribute.rb +184 -0
- data/lib/rom/mapper.rb +63 -100
- data/lib/rom/mapper/attribute_dsl.rb +477 -0
- data/lib/rom/mapper/dsl.rb +120 -0
- data/lib/rom/mapper/model_dsl.rb +55 -0
- data/lib/rom/mapper/version.rb +3 -7
- data/lib/rom/model_builder.rb +99 -0
- data/lib/rom/processor.rb +28 -0
- data/lib/rom/processor/transproc.rb +388 -0
- data/rakelib/benchmark.rake +15 -0
- data/rakelib/mutant.rake +16 -0
- data/rakelib/rubocop.rake +18 -0
- data/rom-mapper.gemspec +7 -6
- data/spec/spec_helper.rb +32 -33
- data/spec/support/constant_leak_finder.rb +14 -0
- data/spec/support/mutant.rb +10 -0
- data/spec/unit/rom/mapper/dsl_spec.rb +467 -0
- data/spec/unit/rom/mapper_spec.rb +83 -0
- data/spec/unit/rom/processor/transproc_spec.rb +448 -0
- metadata +68 -89
- data/.ruby-version +0 -1
- data/Gemfile.devtools +0 -55
- data/config/devtools.yml +0 -2
- data/config/flay.yml +0 -3
- data/config/flog.yml +0 -2
- data/config/mutant.yml +0 -3
- data/config/reek.yml +0 -103
- data/config/rubocop.yml +0 -45
- data/lib/rom/mapper/attribute.rb +0 -31
- data/lib/rom/mapper/dumper.rb +0 -27
- data/lib/rom/mapper/loader.rb +0 -22
- data/lib/rom/mapper/loader/allocator.rb +0 -32
- data/lib/rom/mapper/loader/attribute_writer.rb +0 -23
- data/lib/rom/mapper/loader/object_builder.rb +0 -28
- data/spec/shared/unit/loader_call.rb +0 -13
- data/spec/shared/unit/loader_identity.rb +0 -13
- data/spec/shared/unit/mapper_context.rb +0 -13
- data/spec/unit/rom/mapper/call_spec.rb +0 -32
- data/spec/unit/rom/mapper/class_methods/build_spec.rb +0 -64
- data/spec/unit/rom/mapper/dump_spec.rb +0 -15
- data/spec/unit/rom/mapper/dumper/call_spec.rb +0 -29
- data/spec/unit/rom/mapper/dumper/identity_spec.rb +0 -28
- data/spec/unit/rom/mapper/header/each_spec.rb +0 -28
- data/spec/unit/rom/mapper/header/element_reader_spec.rb +0 -25
- data/spec/unit/rom/mapper/header/keys_spec.rb +0 -32
- data/spec/unit/rom/mapper/identity_from_tuple_spec.rb +0 -15
- data/spec/unit/rom/mapper/identity_spec.rb +0 -15
- data/spec/unit/rom/mapper/load_spec.rb +0 -15
- data/spec/unit/rom/mapper/loader/allocator/call_spec.rb +0 -7
- data/spec/unit/rom/mapper/loader/allocator/identity_spec.rb +0 -7
- data/spec/unit/rom/mapper/loader/attribute_writer/call_spec.rb +0 -7
- data/spec/unit/rom/mapper/loader/attribute_writer/identity_spec.rb +0 -7
- data/spec/unit/rom/mapper/loader/object_builder/call_spec.rb +0 -7
- data/spec/unit/rom/mapper/loader/object_builder/identity_spec.rb +0 -7
- data/spec/unit/rom/mapper/model_spec.rb +0 -11
- data/spec/unit/rom/mapper/new_object_spec.rb +0 -14
@@ -0,0 +1,15 @@
|
|
1
|
+
desc "Run benchmarks (tweak count via COUNT envvar)"
|
2
|
+
task :benchmark do
|
3
|
+
FileList["benchmarks/**/*_bench.rb"].each do |bench|
|
4
|
+
sh "ruby #{bench}"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
namespace :benchmark do
|
9
|
+
desc "Verify benchmarks"
|
10
|
+
task :verify do
|
11
|
+
ENV['VERIFY'] = "true"
|
12
|
+
ENV['COUNT'] = "1"
|
13
|
+
Rake::Task[:benchmark].invoke
|
14
|
+
end
|
15
|
+
end
|
data/rakelib/mutant.rake
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
desc "Run mutant against a specific subject"
|
2
|
+
task :mutant do
|
3
|
+
subject = ARGV.last
|
4
|
+
if subject == 'mutant'
|
5
|
+
abort "usage: rake mutant SUBJECT\nexample: rake mutant ROM::Header"
|
6
|
+
else
|
7
|
+
opts = {
|
8
|
+
'include' => 'lib',
|
9
|
+
'require' => 'rom',
|
10
|
+
'use' => 'rspec',
|
11
|
+
'ignore-subject' => "#{subject}#respond_to_missing?"
|
12
|
+
}.to_a.map { |k, v| "--#{k} #{v}" }.join(' ')
|
13
|
+
|
14
|
+
exec("bundle exec mutant #{opts} #{subject}")
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
begin
|
2
|
+
require "rubocop/rake_task"
|
3
|
+
|
4
|
+
Rake::Task[:default].enhance [:rubocop]
|
5
|
+
|
6
|
+
RuboCop::RakeTask.new do |task|
|
7
|
+
task.options << "--display-cop-names"
|
8
|
+
end
|
9
|
+
|
10
|
+
namespace :rubocop do
|
11
|
+
desc 'Generate a configuration file acting as a TODO list.'
|
12
|
+
task :auto_gen_config do
|
13
|
+
exec "bundle exec rubocop --auto-gen-config"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
rescue LoadError
|
18
|
+
end
|
data/rom-mapper.gemspec
CHANGED
@@ -4,7 +4,7 @@ require File.expand_path('../lib/rom/mapper/version', __FILE__)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |gem|
|
6
6
|
gem.name = "rom-mapper"
|
7
|
-
gem.description = "
|
7
|
+
gem.description = "ROM mapper component"
|
8
8
|
gem.summary = gem.description
|
9
9
|
gem.authors = 'Piotr Solnica'
|
10
10
|
gem.email = 'piotr.solnica@gmail.com'
|
@@ -15,9 +15,10 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.test_files = `git ls-files -- {spec}/*`.split("\n")
|
16
16
|
gem.license = 'MIT'
|
17
17
|
|
18
|
-
gem.add_dependency '
|
19
|
-
gem.add_dependency 'equalizer',
|
20
|
-
gem.add_dependency '
|
21
|
-
|
22
|
-
gem.
|
18
|
+
gem.add_dependency 'transproc', '~> 0.3'
|
19
|
+
gem.add_dependency 'equalizer', '~> 0.0', '>= 0.0.10'
|
20
|
+
gem.add_dependency 'rom-support', '~> 0.1', '>= 0.1.0'
|
21
|
+
|
22
|
+
gem.add_development_dependency 'rake', '~> 10.3'
|
23
|
+
gem.add_development_dependency 'rspec', '~> 3.3'
|
23
24
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,48 +1,47 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
# this is needed for guard to work, not sure why :(
|
4
|
+
require "bundler"
|
5
|
+
Bundler.setup
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
]
|
11
|
-
|
12
|
-
SimpleCov.start do
|
13
|
-
command_name 'spec:unit'
|
14
|
-
|
15
|
-
add_filter 'config'
|
16
|
-
add_filter 'lib/rom/support'
|
17
|
-
add_filter 'spec'
|
18
|
-
end
|
7
|
+
if RUBY_ENGINE == "rbx"
|
8
|
+
require "codeclimate-test-reporter"
|
9
|
+
CodeClimate::TestReporter.start
|
19
10
|
end
|
20
11
|
|
21
12
|
require 'rom-mapper'
|
22
|
-
require 'axiom'
|
23
|
-
|
24
|
-
require 'devtools/spec_helper'
|
25
|
-
require 'bogus/rspec'
|
26
13
|
|
27
|
-
|
28
|
-
|
14
|
+
begin
|
15
|
+
require 'byebug'
|
16
|
+
rescue LoadError
|
29
17
|
end
|
30
18
|
|
31
|
-
|
32
|
-
|
19
|
+
root = Pathname(__FILE__).dirname
|
20
|
+
|
21
|
+
Dir[root.join('support/*.rb').to_s].each do |f|
|
22
|
+
require f
|
23
|
+
end
|
24
|
+
Dir[root.join('shared/*.rb').to_s].each do |f|
|
25
|
+
require f
|
33
26
|
end
|
34
27
|
|
35
|
-
|
28
|
+
# Namespace holding all objects created during specs
|
29
|
+
module Test
|
30
|
+
def self.remove_constants
|
31
|
+
constants.each(&method(:remove_const))
|
32
|
+
end
|
33
|
+
end
|
36
34
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
35
|
+
def T(*args)
|
36
|
+
ROM::Processor::Transproc::Functions[*args]
|
37
|
+
end
|
40
38
|
|
41
|
-
|
39
|
+
RSpec.configure do |config|
|
40
|
+
config.after do
|
41
|
+
Test.remove_constants
|
42
|
+
end
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
47
|
-
}
|
44
|
+
config.around do |example|
|
45
|
+
ConstantLeakFinder.find(example)
|
46
|
+
end
|
48
47
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Finds leaking constants created during ROM specs
|
2
|
+
module ConstantLeakFinder
|
3
|
+
def self.find(example)
|
4
|
+
constants = Object.constants
|
5
|
+
|
6
|
+
example.run
|
7
|
+
|
8
|
+
added_constants = (Object.constants - constants)
|
9
|
+
added = added_constants.map(&Object.method(:const_get))
|
10
|
+
if added.any? { |mod| mod.ancestors.map(&:name).grep(/\AROM/).any? }
|
11
|
+
raise "Leaking constants: #{added_constants.inspect}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,467 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ROM::Mapper do
|
4
|
+
subject(:mapper) do
|
5
|
+
klass = Class.new(parent)
|
6
|
+
options.each do |k, v|
|
7
|
+
klass.send(k, v)
|
8
|
+
end
|
9
|
+
klass
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:parent) { Class.new(ROM::Mapper) }
|
13
|
+
|
14
|
+
let(:options) { {} }
|
15
|
+
let(:header) { mapper.header }
|
16
|
+
|
17
|
+
let(:expected_header) { ROM::Header.coerce(attributes) }
|
18
|
+
|
19
|
+
describe '#attribute' do
|
20
|
+
context 'simple attribute' do
|
21
|
+
let(:attributes) { [[:name]] }
|
22
|
+
|
23
|
+
it 'adds an attribute for the header' do
|
24
|
+
mapper.attribute :name
|
25
|
+
|
26
|
+
expect(header).to eql(expected_header)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'aliased attribute' do
|
31
|
+
let(:attributes) { [[:name, from: :user_name]] }
|
32
|
+
|
33
|
+
it 'adds an aliased attribute for the header' do
|
34
|
+
mapper.attribute :name, from: :user_name
|
35
|
+
|
36
|
+
expect(header).to eql(expected_header)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'prefixed attribute' do
|
41
|
+
let(:attributes) { [[:name, from: :user_name]] }
|
42
|
+
let(:options) { { prefix: :user } }
|
43
|
+
|
44
|
+
it 'adds an aliased attribute for the header using configured :prefix' do
|
45
|
+
mapper.attribute :name
|
46
|
+
|
47
|
+
expect(header).to eql(expected_header)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'prefixed attribute using custom separator' do
|
52
|
+
let(:attributes) { [[:name, from: :'u.name']] }
|
53
|
+
let(:options) { { prefix: :u, prefix_separator: '.' } }
|
54
|
+
|
55
|
+
it 'adds an aliased attribute for the header using configured :prefix' do
|
56
|
+
mapper.attribute :name
|
57
|
+
|
58
|
+
expect(header).to eql(expected_header)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'symbolized attribute' do
|
63
|
+
let(:attributes) { [[:name, from: 'name']] }
|
64
|
+
let(:options) { { symbolize_keys: true } }
|
65
|
+
|
66
|
+
it 'adds an attribute with symbolized alias' do
|
67
|
+
mapper.attribute :name
|
68
|
+
|
69
|
+
expect(header).to eql(expected_header)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'reject_keys' do
|
75
|
+
let(:attributes) { [[:name, type: :string]] }
|
76
|
+
let(:options) { { reject_keys: true } }
|
77
|
+
|
78
|
+
it 'sets rejected_keys for the header' do
|
79
|
+
mapper.reject_keys true
|
80
|
+
mapper.attribute :name, type: :string
|
81
|
+
|
82
|
+
expect(header).to eql(expected_header)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'overriding inherited attributes' do
|
87
|
+
context 'when name matches' do
|
88
|
+
let(:attributes) { [[:name, type: :string]] }
|
89
|
+
|
90
|
+
it 'excludes the inherited attribute' do
|
91
|
+
parent.attribute :name
|
92
|
+
|
93
|
+
mapper.attribute :name, type: :string
|
94
|
+
|
95
|
+
expect(header).to eql(expected_header)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'when alias matches' do
|
100
|
+
let(:attributes) { [[:name, from: 'name', type: :string]] }
|
101
|
+
|
102
|
+
it 'excludes the inherited attribute' do
|
103
|
+
parent.attribute 'name'
|
104
|
+
|
105
|
+
mapper.attribute :name, from: 'name', type: :string
|
106
|
+
|
107
|
+
expect(header).to eql(expected_header)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'when name in a wrapped attribute matches' do
|
112
|
+
let(:attributes) do
|
113
|
+
[
|
114
|
+
[:city, type: :hash, wrap: true, header: [[:name, from: :city_name]]]
|
115
|
+
]
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'excludes the inherited attribute' do
|
119
|
+
parent.attribute :city_name
|
120
|
+
|
121
|
+
mapper.wrap :city do
|
122
|
+
attribute :name, from: :city_name
|
123
|
+
end
|
124
|
+
|
125
|
+
expect(header).to eql(expected_header)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'when name in a grouped attribute matches' do
|
130
|
+
let(:attributes) do
|
131
|
+
[
|
132
|
+
[:tags, type: :array, group: true, header: [[:name, from: :tag_name]]]
|
133
|
+
]
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'excludes the inherited attribute' do
|
137
|
+
parent.attribute :tag_name
|
138
|
+
|
139
|
+
mapper.group :tags do
|
140
|
+
attribute :name, from: :tag_name
|
141
|
+
end
|
142
|
+
|
143
|
+
expect(header).to eql(expected_header)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context 'when name in a hash attribute matches' do
|
148
|
+
let(:attributes) do
|
149
|
+
[
|
150
|
+
[:city, type: :hash, header: [[:name, from: :city_name]]]
|
151
|
+
]
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'excludes the inherited attribute' do
|
155
|
+
parent.attribute :city
|
156
|
+
|
157
|
+
mapper.embedded :city, type: :hash do
|
158
|
+
attribute :name, from: :city_name
|
159
|
+
end
|
160
|
+
|
161
|
+
expect(header).to eql(expected_header)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'when name of an array attribute matches' do
|
166
|
+
let(:attributes) do
|
167
|
+
[
|
168
|
+
[:tags, type: :array, header: [[:name, from: :tag_name]]]
|
169
|
+
]
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'excludes the inherited attribute' do
|
173
|
+
parent.attribute :tags
|
174
|
+
|
175
|
+
mapper.embedded :tags, type: :array do
|
176
|
+
attribute :name, from: :tag_name
|
177
|
+
end
|
178
|
+
|
179
|
+
expect(header).to eql(expected_header)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe '#exclude' do
|
185
|
+
let(:attributes) { [[:name, from: 'name']] }
|
186
|
+
|
187
|
+
it 'removes an attribute from the inherited header' do
|
188
|
+
mapper.attribute :name, from: 'name'
|
189
|
+
expect(header).to eql(expected_header)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe '#embedded' do
|
194
|
+
context 'when :type is set to :hash' do
|
195
|
+
let(:attributes) { [[:city, type: :hash, header: [[:name]]]] }
|
196
|
+
|
197
|
+
it 'adds an embedded hash attribute' do
|
198
|
+
mapper.embedded :city, type: :hash do
|
199
|
+
attribute :name
|
200
|
+
end
|
201
|
+
|
202
|
+
expect(header).to eql(expected_header)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context 'when :type is set to :array' do
|
207
|
+
let(:attributes) { [[:tags, type: :array, header: [[:name]]]] }
|
208
|
+
|
209
|
+
it 'adds an embedded array attribute' do
|
210
|
+
mapper.embedded :tags, type: :array do
|
211
|
+
attribute :name
|
212
|
+
end
|
213
|
+
|
214
|
+
expect(header).to eql(expected_header)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe '#wrap' do
|
220
|
+
let(:attributes) { [[:city, type: :hash, wrap: true, header: [[:name]]]] }
|
221
|
+
|
222
|
+
it 'adds an wrapped hash attribute using a block to define attributes' do
|
223
|
+
mapper.wrap :city do
|
224
|
+
attribute :name
|
225
|
+
end
|
226
|
+
|
227
|
+
expect(header).to eql(expected_header)
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'adds an wrapped hash attribute using a options define attributes' do
|
231
|
+
mapper.wrap city: [:name]
|
232
|
+
|
233
|
+
expect(header).to eql(expected_header)
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'raises an exception when using a block and options to define attributes' do
|
237
|
+
expect {
|
238
|
+
mapper.wrap(city: [:name]) { attribute :other_name }
|
239
|
+
}.to raise_error(ROM::MapperMisconfiguredError)
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'raises an exception when using options and a mapper to define attributes' do
|
243
|
+
task_mapper = Class.new(ROM::Mapper) { attribute :title }
|
244
|
+
expect {
|
245
|
+
mapper.wrap city: [:name], mapper: task_mapper
|
246
|
+
}.to raise_error(ROM::MapperMisconfiguredError)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
describe '#group' do
|
251
|
+
let(:attributes) { [[:tags, type: :array, group: true, header: [[:name]]]] }
|
252
|
+
|
253
|
+
it 'adds a group attribute using a block to define attributes' do
|
254
|
+
mapper.group :tags do
|
255
|
+
attribute :name
|
256
|
+
end
|
257
|
+
|
258
|
+
expect(header).to eql(expected_header)
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'adds a group attribute using a options define attributes' do
|
262
|
+
mapper.group tags: [:name]
|
263
|
+
|
264
|
+
expect(header).to eql(expected_header)
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'raises an exception when using a block and options to define attributes' do
|
268
|
+
expect {
|
269
|
+
mapper.group(cities: [:name]) { attribute :other_name }
|
270
|
+
}.to raise_error(ROM::MapperMisconfiguredError)
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'raises an exception when using options and a mapper to define attributes' do
|
274
|
+
task_mapper = Class.new(ROM::Mapper) { attribute :title }
|
275
|
+
expect {
|
276
|
+
mapper.group cities: [:name], mapper: task_mapper
|
277
|
+
}.to raise_error(ROM::MapperMisconfiguredError)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
describe 'top-level :prefix option' do
|
282
|
+
let(:options) do
|
283
|
+
{ prefix: :user }
|
284
|
+
end
|
285
|
+
|
286
|
+
context 'when no attribute overrides top-level setting' do
|
287
|
+
let(:attributes) do
|
288
|
+
[
|
289
|
+
[:name, from: :user_name],
|
290
|
+
[:address, from: :user_address, type: :hash, header: [
|
291
|
+
[:city, from: :user_city]]
|
292
|
+
],
|
293
|
+
[:contact, type: :hash, wrap: true, header: [
|
294
|
+
[:mobile, from: :user_mobile]]
|
295
|
+
],
|
296
|
+
[:tasks, type: :array, group: true, header: [
|
297
|
+
[:title, from: :user_title]]
|
298
|
+
]
|
299
|
+
]
|
300
|
+
end
|
301
|
+
|
302
|
+
it 'sets aliased attributes using prefix automatically' do
|
303
|
+
mapper.attribute :name
|
304
|
+
|
305
|
+
mapper.embedded :address, type: :hash do
|
306
|
+
attribute :city
|
307
|
+
end
|
308
|
+
|
309
|
+
mapper.wrap :contact do
|
310
|
+
attribute :mobile
|
311
|
+
end
|
312
|
+
|
313
|
+
mapper.group :tasks do
|
314
|
+
attribute :title
|
315
|
+
end
|
316
|
+
|
317
|
+
expect(header).to eql(expected_header)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
context 'when an attribute overrides top-level setting' do
|
322
|
+
let(:attributes) do
|
323
|
+
[
|
324
|
+
[:name, from: :user_name],
|
325
|
+
[:birthday, from: :user_birthday, type: :hash, header: [
|
326
|
+
[:year, from: :bd_year],
|
327
|
+
[:month, from: :bd_month],
|
328
|
+
[:day, from: :bd_day]]
|
329
|
+
],
|
330
|
+
[:address, from: :user_address, type: :hash, header: [[:city]]],
|
331
|
+
[:contact, type: :hash, wrap: true, header: [
|
332
|
+
[:mobile, from: :contact_mobile]]
|
333
|
+
],
|
334
|
+
[:tasks, type: :array, group: true, header: [
|
335
|
+
[:title, from: :task_title]]
|
336
|
+
]
|
337
|
+
]
|
338
|
+
end
|
339
|
+
|
340
|
+
it 'excludes from aliasing the ones which override it' do
|
341
|
+
mapper.attribute :name
|
342
|
+
|
343
|
+
mapper.embedded :birthday, type: :hash, prefix: :bd do
|
344
|
+
attribute :year
|
345
|
+
attribute :month
|
346
|
+
attribute :day
|
347
|
+
end
|
348
|
+
|
349
|
+
mapper.embedded :address, type: :hash, prefix: false do
|
350
|
+
attribute :city
|
351
|
+
end
|
352
|
+
|
353
|
+
mapper.wrap :contact, prefix: :contact do
|
354
|
+
attribute :mobile
|
355
|
+
end
|
356
|
+
|
357
|
+
mapper.group :tasks, prefix: :task do
|
358
|
+
attribute :title
|
359
|
+
end
|
360
|
+
|
361
|
+
expect(header).to eql(expected_header)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
context 'reusing mappers' do
|
367
|
+
describe '#group' do
|
368
|
+
let(:task_mapper) do
|
369
|
+
Class.new(ROM::Mapper) { attribute :title }
|
370
|
+
end
|
371
|
+
|
372
|
+
let(:attributes) do
|
373
|
+
[
|
374
|
+
[:name],
|
375
|
+
[:tasks, type: :array, group: true, header: task_mapper.header]
|
376
|
+
]
|
377
|
+
end
|
378
|
+
|
379
|
+
it 'uses other mapper header' do
|
380
|
+
mapper.attribute :name
|
381
|
+
mapper.group :tasks, mapper: task_mapper
|
382
|
+
|
383
|
+
expect(header).to eql(expected_header)
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
describe '#wrap' do
|
388
|
+
let(:task_mapper) do
|
389
|
+
Class.new(ROM::Mapper) { attribute :title }
|
390
|
+
end
|
391
|
+
|
392
|
+
let(:attributes) do
|
393
|
+
[
|
394
|
+
[:name],
|
395
|
+
[:task, type: :hash, wrap: true, header: task_mapper.header]
|
396
|
+
]
|
397
|
+
end
|
398
|
+
|
399
|
+
it 'uses other mapper header' do
|
400
|
+
mapper.attribute :name
|
401
|
+
mapper.wrap :task, mapper: task_mapper
|
402
|
+
|
403
|
+
expect(header).to eql(expected_header)
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
describe '#embedded' do
|
408
|
+
let(:task_mapper) do
|
409
|
+
Class.new(ROM::Mapper) { attribute :title }
|
410
|
+
end
|
411
|
+
|
412
|
+
let(:attributes) do
|
413
|
+
[
|
414
|
+
[:name],
|
415
|
+
[:task, type: :hash, header: task_mapper.header]
|
416
|
+
]
|
417
|
+
end
|
418
|
+
|
419
|
+
it 'uses other mapper header' do
|
420
|
+
mapper.attribute :name
|
421
|
+
mapper.embedded :task, mapper: task_mapper, type: :hash
|
422
|
+
|
423
|
+
expect(header).to eql(expected_header)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
describe '#combine' do
|
429
|
+
let(:attributes) do
|
430
|
+
[
|
431
|
+
[:title],
|
432
|
+
[:tasks, combine: true, type: :array, header: [[:title]]]
|
433
|
+
]
|
434
|
+
end
|
435
|
+
|
436
|
+
it 'adds combine attributes' do
|
437
|
+
mapper.attribute :title
|
438
|
+
|
439
|
+
mapper.combine :tasks, on: { title: :title } do
|
440
|
+
attribute :title
|
441
|
+
end
|
442
|
+
|
443
|
+
expect(header).to eql(expected_header)
|
444
|
+
end
|
445
|
+
|
446
|
+
it 'works without a block' do
|
447
|
+
expected_header = ROM::Header.coerce(
|
448
|
+
[
|
449
|
+
[:title],
|
450
|
+
[:tasks, combine: true, type: :array, header: []]
|
451
|
+
]
|
452
|
+
)
|
453
|
+
|
454
|
+
mapper.attribute :title
|
455
|
+
|
456
|
+
mapper.combine :tasks, on: { title: :title }
|
457
|
+
|
458
|
+
expect(header).to eql(expected_header)
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
describe '#method_missing' do
|
463
|
+
it 'responds to DSL methods' do
|
464
|
+
expect(mapper).to respond_to(:attribute)
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|