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
data/lib/rom/mapper/attribute.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module ROM
|
4
|
-
class Mapper
|
5
|
-
|
6
|
-
# Represents a mapping attribute
|
7
|
-
#
|
8
|
-
# @private
|
9
|
-
class Attribute < Struct.new(:name, :field)
|
10
|
-
include Adamantium, Equalizer.new(:name, :field)
|
11
|
-
|
12
|
-
# @api private
|
13
|
-
def self.coerce(input, mapping = nil)
|
14
|
-
field = Axiom::Attribute.coerce(input)
|
15
|
-
new(mapping || field.name, field)
|
16
|
-
end
|
17
|
-
|
18
|
-
# @api private
|
19
|
-
def mapping
|
20
|
-
{ tuple_key => name }
|
21
|
-
end
|
22
|
-
|
23
|
-
# @api private
|
24
|
-
def tuple_key
|
25
|
-
field.name
|
26
|
-
end
|
27
|
-
|
28
|
-
end # Attribute
|
29
|
-
|
30
|
-
end # Mapper
|
31
|
-
end # ROM
|
data/lib/rom/mapper/dumper.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module ROM
|
4
|
-
class Mapper
|
5
|
-
|
6
|
-
# Dumps an object back into a tuple
|
7
|
-
#
|
8
|
-
# @private
|
9
|
-
class Dumper
|
10
|
-
include Concord::Public.new(:header), Adamantium
|
11
|
-
|
12
|
-
# @api private
|
13
|
-
def call(object)
|
14
|
-
header.each_with_object([]) { |attribute, tuple|
|
15
|
-
tuple << object.send(attribute.name)
|
16
|
-
}
|
17
|
-
end
|
18
|
-
|
19
|
-
# @api private
|
20
|
-
def identity(object)
|
21
|
-
header.keys.map { |key| object.send("#{key.name}") }
|
22
|
-
end
|
23
|
-
|
24
|
-
end # Dumper
|
25
|
-
|
26
|
-
end # Mapper
|
27
|
-
end # ROM
|
data/lib/rom/mapper/loader.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module ROM
|
4
|
-
class Mapper
|
5
|
-
|
6
|
-
# Abstract loader class
|
7
|
-
#
|
8
|
-
# @private
|
9
|
-
class Loader
|
10
|
-
include Concord::Public.new(:header, :model), Adamantium, AbstractType
|
11
|
-
|
12
|
-
abstract_method :call
|
13
|
-
|
14
|
-
# @api public
|
15
|
-
def identity(tuple)
|
16
|
-
header.keys.map { |key| tuple[key.name] }
|
17
|
-
end
|
18
|
-
|
19
|
-
end # Loader
|
20
|
-
|
21
|
-
end # Mapper
|
22
|
-
end # ROM
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module ROM
|
4
|
-
class Mapper
|
5
|
-
class Loader
|
6
|
-
|
7
|
-
# Loader class which doesn't call initialize
|
8
|
-
#
|
9
|
-
# @private
|
10
|
-
class Allocator < self
|
11
|
-
|
12
|
-
# @api private
|
13
|
-
def call(tuple)
|
14
|
-
allocate { |attribute, object|
|
15
|
-
object.instance_variable_set(
|
16
|
-
"@#{attribute.name}", tuple[attribute.name]
|
17
|
-
)
|
18
|
-
}
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
# @api private
|
24
|
-
def allocate(&block)
|
25
|
-
header.each_with_object(model.allocate, &block)
|
26
|
-
end
|
27
|
-
|
28
|
-
end # Allocator
|
29
|
-
|
30
|
-
end # Loader
|
31
|
-
end # Mapper
|
32
|
-
end # ROM
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module ROM
|
4
|
-
class Mapper
|
5
|
-
class Loader
|
6
|
-
|
7
|
-
# Special type of Allocator loader which uses attribute writers
|
8
|
-
#
|
9
|
-
# @private
|
10
|
-
class AttributeWriter < Allocator
|
11
|
-
|
12
|
-
# @api private
|
13
|
-
def call(tuple)
|
14
|
-
allocate { |attribute, object|
|
15
|
-
object.public_send("#{attribute.name}=", tuple[attribute.name])
|
16
|
-
}
|
17
|
-
end
|
18
|
-
|
19
|
-
end # AttributeWriter
|
20
|
-
|
21
|
-
end # Loader
|
22
|
-
end # Mapper
|
23
|
-
end # ROM
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module ROM
|
4
|
-
class Mapper
|
5
|
-
class Loader
|
6
|
-
|
7
|
-
# Loader class that calls initialize
|
8
|
-
#
|
9
|
-
# @private
|
10
|
-
class ObjectBuilder < self
|
11
|
-
|
12
|
-
# @api private
|
13
|
-
def call(tuple)
|
14
|
-
model.new(attributes(tuple))
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
# @api private
|
20
|
-
def attributes(tuple)
|
21
|
-
Hash[header.attribute_names.map { |name| [name, tuple[name]] }]
|
22
|
-
end
|
23
|
-
|
24
|
-
end # ObjectBuilder
|
25
|
-
|
26
|
-
end # Loader
|
27
|
-
end # Mapper
|
28
|
-
end # ROM
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
shared_examples_for 'Mapper::Loader#call' do
|
4
|
-
subject(:loader) { described_class.new(header, model) }
|
5
|
-
|
6
|
-
let(:header) { Mapper::Header.build([[:id, Integer], [:name, String]]) }
|
7
|
-
let(:tuple) { Hash[id: 1, name: 'Jane', something: 'foo'] }
|
8
|
-
let(:model) { mock_model(:id, :name) }
|
9
|
-
|
10
|
-
it 'returns loaded object' do
|
11
|
-
expect(loader.call(tuple)).to eq(model.new(id: 1, name: 'Jane'))
|
12
|
-
end
|
13
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
shared_examples_for 'Mapper::Loader#identity' do
|
4
|
-
subject(:loader) { described_class.new(header, model) }
|
5
|
-
|
6
|
-
let(:header) { Mapper::Header.build([[:id, Integer], [:name, String]], keys: [:id]) }
|
7
|
-
let(:tuple) { Hash[id: 1, name: 'Jane'] }
|
8
|
-
let(:model) { mock_model(:id, :name) }
|
9
|
-
|
10
|
-
it "returns object's identity" do
|
11
|
-
expect(loader.identity(tuple)).to eq([1])
|
12
|
-
end
|
13
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
shared_context 'Mapper' do
|
4
|
-
let(:mapper) { described_class.new(header, loader, dumper) }
|
5
|
-
|
6
|
-
let(:header) { fake(:header) { Mapper::Header } }
|
7
|
-
let(:loader) { fake(:loader, model: model) { Mapper::Loader } }
|
8
|
-
let(:dumper) { fake(:dumper) { Mapper::Dumper } }
|
9
|
-
let(:data) { [1, 'Jane'] }
|
10
|
-
let(:tuple) { Hash[id: 1, name: 'Jane'] }
|
11
|
-
let(:object) { model.new(id: 1, name: 'Jane') }
|
12
|
-
let(:model) { mock_model(:id, :name) }
|
13
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Mapper, '#call' do
|
6
|
-
subject { object.call(relation) }
|
7
|
-
|
8
|
-
let(:object) {
|
9
|
-
Mapper.build(header, model)
|
10
|
-
}
|
11
|
-
|
12
|
-
let(:header) {
|
13
|
-
Mapper::Header.build(
|
14
|
-
[[:user_id, Integer], [:user_name, String], [:age, Integer]],
|
15
|
-
map: { user_id: :id, user_name: :name }
|
16
|
-
)
|
17
|
-
}
|
18
|
-
|
19
|
-
let(:model) {
|
20
|
-
OpenStruct
|
21
|
-
}
|
22
|
-
|
23
|
-
let(:relation) {
|
24
|
-
Axiom::Relation::Base.new(:users, [
|
25
|
-
[:user_id, Integer], [:user_name, String], [:email, String], [:age, Integer]
|
26
|
-
])
|
27
|
-
}
|
28
|
-
|
29
|
-
it 'renames relation' do
|
30
|
-
expect(subject.header.map(&:name)).to eq([:id, :name, :age])
|
31
|
-
end
|
32
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Mapper, '.build' do
|
6
|
-
subject { described_class.build(header, model, options) }
|
7
|
-
|
8
|
-
let(:model) { mock_model(:name) }
|
9
|
-
let(:attributes) { [[:user_name, String]] }
|
10
|
-
let(:options) { Hash[map: { user_name: :name }] }
|
11
|
-
|
12
|
-
describe 'when header is a primitive' do
|
13
|
-
let(:header) { attributes }
|
14
|
-
|
15
|
-
its(:model) { should be(model) }
|
16
|
-
its(:loader) { should be_instance_of(Mapper::LOADERS[:allocator]) }
|
17
|
-
its(:dumper) { should be_instance_of(Mapper::Dumper) }
|
18
|
-
|
19
|
-
it 'builds correct header' do
|
20
|
-
expect(subject.header.mapping).to eql(options[:map])
|
21
|
-
end
|
22
|
-
|
23
|
-
let(:object) { model.new(name: 'Jane') }
|
24
|
-
let(:params) { Hash[name: 'Jane'] }
|
25
|
-
|
26
|
-
specify do
|
27
|
-
expect(subject.load(params)).to eq(object)
|
28
|
-
end
|
29
|
-
|
30
|
-
specify do
|
31
|
-
expect(subject.dump(object)).to eq(params.values)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
describe 'when header is a mapper header instance' do
|
36
|
-
let(:header) { Mapper::Header.build(attributes) }
|
37
|
-
let(:options) { Hash.new }
|
38
|
-
|
39
|
-
its(:model) { should be(model) }
|
40
|
-
its(:loader) { should be_instance_of(Mapper::LOADERS[:allocator]) }
|
41
|
-
its(:dumper) { should be_instance_of(Mapper::Dumper) }
|
42
|
-
its(:header) { should be(header) }
|
43
|
-
end
|
44
|
-
|
45
|
-
describe 'when options has custom loader' do
|
46
|
-
let(:header) { Mapper::Header.build(attributes) }
|
47
|
-
let(:options) { Hash[loader: :object_builder] }
|
48
|
-
|
49
|
-
its(:model) { should be(model) }
|
50
|
-
its(:loader) { should be_instance_of(Mapper::LOADERS[:object_builder]) }
|
51
|
-
its(:dumper) { should be_instance_of(Mapper::Dumper) }
|
52
|
-
its(:header) { should be(header) }
|
53
|
-
end
|
54
|
-
|
55
|
-
describe 'loader is set to :attribute_writer' do
|
56
|
-
let(:header) { Mapper::Header.build(attributes) }
|
57
|
-
let(:options) { Hash[loader: :attribute_writer] }
|
58
|
-
|
59
|
-
its(:model) { should be(model) }
|
60
|
-
its(:loader) { should be_instance_of(Mapper::LOADERS[:attribute_writer]) }
|
61
|
-
its(:dumper) { should be_instance_of(Mapper::Dumper) }
|
62
|
-
its(:header) { should be(header) }
|
63
|
-
end
|
64
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Mapper, '#dump' do
|
6
|
-
include_context 'Mapper'
|
7
|
-
|
8
|
-
it 'dumps the object into data tuple' do
|
9
|
-
stub(dumper).call(object) { data }
|
10
|
-
|
11
|
-
expect(mapper.dump(object)).to be(data)
|
12
|
-
|
13
|
-
dumper.should have_received.call(object)
|
14
|
-
end
|
15
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Mapper::Dumper, '#call' do
|
6
|
-
subject(:dumper) { described_class.new(header) }
|
7
|
-
|
8
|
-
let(:header) { Mapper::Header.build([[:uid, Integer], [:name, String]], map: { uid: :id }, keys: [:uid]) }
|
9
|
-
let(:data) { Hash[id: 1, name: 'Jane'] }
|
10
|
-
let(:model) { mock_model(:id, :name) }
|
11
|
-
let(:object) { model.new(data) }
|
12
|
-
|
13
|
-
context 'with public attribute readers' do
|
14
|
-
it 'returns dumped tuple' do
|
15
|
-
expect(dumper.call(object)).to eq([1, 'Jane'])
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
context 'with private attribute readers' do
|
20
|
-
before do
|
21
|
-
model.send(:private, :id)
|
22
|
-
model.send(:private, :name)
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'returns dumped tuple' do
|
26
|
-
expect(dumper.call(object)).to eq([1, 'Jane'])
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Mapper::Dumper, '#identity' do
|
6
|
-
subject(:dumper) { described_class.new(header) }
|
7
|
-
|
8
|
-
let(:header) { Mapper::Header.build([[:uid, Integer], [:name, String]], map: { uid: :id }, keys: [:uid]) }
|
9
|
-
let(:data) { Hash[id: 1, name: 'Jane'] }
|
10
|
-
let(:model) { mock_model(:id, :name) }
|
11
|
-
let(:object) { model.new(data) }
|
12
|
-
|
13
|
-
context 'with public attribute readers' do
|
14
|
-
it "returns object's identity" do
|
15
|
-
expect(dumper.identity(object)).to eq([1])
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
context 'with private attribute readers' do
|
20
|
-
before do
|
21
|
-
model.send(:private, :id)
|
22
|
-
end
|
23
|
-
|
24
|
-
it "returns object's identity" do
|
25
|
-
expect(dumper.identity(object)).to eq([1])
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Mapper::Header, '#each' do
|
6
|
-
let(:object) { Mapper::Header.build(attributes) }
|
7
|
-
let(:attributes) { [[:id, Integer], [:name, String]] }
|
8
|
-
let(:id) { object[:id] }
|
9
|
-
let(:name) { object[:name] }
|
10
|
-
|
11
|
-
context 'with a block' do
|
12
|
-
subject { object.each { |attribute| result << attribute } }
|
13
|
-
|
14
|
-
let(:result) { [] }
|
15
|
-
|
16
|
-
it { should be(object) }
|
17
|
-
|
18
|
-
specify do
|
19
|
-
expect { subject }.to change { result }.from([]).to([id, name])
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
context 'without a block' do
|
24
|
-
subject { object.each }
|
25
|
-
|
26
|
-
it { should be_instance_of(Enumerator) }
|
27
|
-
end
|
28
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Mapper::Header, '#[]' do
|
6
|
-
subject { object[name] }
|
7
|
-
|
8
|
-
let(:object) { Mapper::Header.build(attributes) }
|
9
|
-
let(:attributes) { [[:id, Integer]] }
|
10
|
-
|
11
|
-
context 'when attribute exists' do
|
12
|
-
let(:name) { :id }
|
13
|
-
let(:id) { Mapper::Attribute.coerce(attributes.first) }
|
14
|
-
|
15
|
-
it { should eql(id) }
|
16
|
-
end
|
17
|
-
|
18
|
-
context 'when attribute does not exist' do
|
19
|
-
let(:name) { :not_here }
|
20
|
-
|
21
|
-
specify do
|
22
|
-
expect { subject }.to raise_error(KeyError)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|