rom 0.4.2 → 0.5.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 +4 -4
- data/.rubocop.yml +81 -0
- data/.travis.yml +2 -1
- data/CHANGELOG.md +41 -0
- data/Gemfile +12 -8
- data/Guardfile +17 -11
- data/README.md +7 -7
- data/Rakefile +29 -0
- data/lib/rom.rb +9 -66
- data/lib/rom/adapter.rb +45 -12
- data/lib/rom/adapter/memory.rb +0 -4
- data/lib/rom/adapter/memory/commands.rb +0 -10
- data/lib/rom/adapter/memory/dataset.rb +18 -6
- data/lib/rom/adapter/memory/storage.rb +0 -3
- data/lib/rom/command_registry.rb +24 -43
- data/lib/rom/commands.rb +5 -6
- data/lib/rom/commands/create.rb +5 -5
- data/lib/rom/commands/delete.rb +8 -6
- data/lib/rom/commands/result.rb +82 -0
- data/lib/rom/commands/update.rb +5 -4
- data/lib/rom/commands/with_options.rb +1 -4
- data/lib/rom/config.rb +70 -0
- data/lib/rom/env.rb +11 -3
- data/lib/rom/global.rb +107 -0
- data/lib/rom/header.rb +122 -89
- data/lib/rom/header/attribute.rb +148 -0
- data/lib/rom/mapper.rb +46 -67
- data/lib/rom/mapper_builder.rb +20 -73
- data/lib/rom/mapper_builder/mapper_dsl.rb +114 -0
- data/lib/rom/mapper_builder/model_dsl.rb +29 -0
- data/lib/rom/mapper_registry.rb +21 -0
- data/lib/rom/model_builder.rb +11 -17
- data/lib/rom/processor.rb +28 -0
- data/lib/rom/processor/transproc.rb +105 -0
- data/lib/rom/reader.rb +81 -21
- data/lib/rom/reader_builder.rb +14 -4
- data/lib/rom/relation.rb +19 -5
- data/lib/rom/relation_builder.rb +20 -6
- data/lib/rom/repository.rb +0 -2
- data/lib/rom/setup.rb +156 -0
- data/lib/rom/{boot → setup}/base_relation_dsl.rb +4 -8
- data/lib/rom/setup/command_dsl.rb +46 -0
- data/lib/rom/setup/finalize.rb +125 -0
- data/lib/rom/setup/mapper_dsl.rb +19 -0
- data/lib/rom/{boot → setup}/relation_dsl.rb +1 -4
- data/lib/rom/setup/schema_dsl.rb +33 -0
- data/lib/rom/support/registry.rb +10 -6
- data/lib/rom/version.rb +1 -1
- data/rom.gemspec +3 -1
- data/spec/integration/adapters/extending_relations_spec.rb +0 -2
- data/spec/integration/commands/create_spec.rb +2 -9
- data/spec/integration/commands/delete_spec.rb +4 -5
- data/spec/integration/commands/error_handling_spec.rb +4 -3
- data/spec/integration/commands/update_spec.rb +3 -8
- data/spec/integration/mappers/deep_embedded_spec.rb +52 -0
- data/spec/integration/mappers/definition_dsl_spec.rb +0 -118
- data/spec/integration/mappers/embedded_spec.rb +82 -0
- data/spec/integration/mappers/group_spec.rb +170 -0
- data/spec/integration/mappers/prefixing_attributes_spec.rb +2 -2
- data/spec/integration/mappers/renaming_attributes_spec.rb +8 -6
- data/spec/integration/mappers/symbolizing_attributes_spec.rb +80 -0
- data/spec/integration/mappers/wrap_spec.rb +162 -0
- data/spec/integration/multi_repo_spec.rb +64 -0
- data/spec/integration/relations/reading_spec.rb +12 -8
- data/spec/integration/relations/registry_dsl_spec.rb +1 -3
- data/spec/integration/schema_spec.rb +10 -0
- data/spec/integration/setup_spec.rb +57 -6
- data/spec/spec_helper.rb +2 -1
- data/spec/unit/config_spec.rb +60 -0
- data/spec/unit/rom/adapter/memory/dataset_spec.rb +52 -0
- data/spec/unit/rom/adapter_spec.rb +31 -11
- data/spec/unit/rom/header_spec.rb +60 -16
- data/spec/unit/rom/mapper_builder_spec.rb +311 -0
- data/spec/unit/rom/mapper_registry_spec.rb +25 -0
- data/spec/unit/rom/mapper_spec.rb +4 -5
- data/spec/unit/rom/model_builder_spec.rb +15 -13
- data/spec/unit/rom/processor/transproc_spec.rb +331 -0
- data/spec/unit/rom/reader_spec.rb +73 -0
- data/spec/unit/rom/registry_spec.rb +38 -0
- data/spec/unit/rom/relation_spec.rb +0 -1
- data/spec/unit/rom/setup_spec.rb +55 -0
- data/spec/unit/rom_spec.rb +14 -0
- metadata +62 -22
- data/Gemfile.devtools +0 -71
- data/lib/rom/boot.rb +0 -197
- data/lib/rom/boot/command_dsl.rb +0 -48
- data/lib/rom/boot/dsl.rb +0 -37
- data/lib/rom/boot/mapper_dsl.rb +0 -23
- data/lib/rom/boot/schema_dsl.rb +0 -27
- data/lib/rom/ra.rb +0 -172
- data/lib/rom/ra/operation/group.rb +0 -47
- data/lib/rom/ra/operation/join.rb +0 -39
- data/lib/rom/ra/operation/wrap.rb +0 -45
- data/lib/rom/transformer.rb +0 -77
- data/spec/integration/ra/group_spec.rb +0 -46
- data/spec/integration/ra/join_spec.rb +0 -50
- data/spec/integration/ra/wrap_spec.rb +0 -37
- data/spec/unit/rom/ra/operation/group_spec.rb +0 -55
- data/spec/unit/rom/ra/operation/wrap_spec.rb +0 -29
- data/spec/unit/rom/transformer_spec.rb +0 -41
data/lib/rom/config.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module ROM
|
2
|
+
# Helper class used by ROM internally to deal with various configuration hashes
|
3
|
+
#
|
4
|
+
# @private
|
5
|
+
class Config
|
6
|
+
BASE_OPTIONS = [
|
7
|
+
:adapter,
|
8
|
+
:database,
|
9
|
+
:password,
|
10
|
+
:username,
|
11
|
+
:hostname,
|
12
|
+
:root
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
# Builds a configuration hash from a flat database config hash or a string
|
16
|
+
#
|
17
|
+
# This is used to support typical database.yml-complaint configs. It also
|
18
|
+
# uses adapter interface for things that are adapter-specific like handling
|
19
|
+
# schema naming.
|
20
|
+
#
|
21
|
+
# @param [Hash,String]
|
22
|
+
#
|
23
|
+
# @return [Hash]
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
def self.build(config, options = {})
|
27
|
+
return config_hash(config, options) if config.is_a?(String)
|
28
|
+
|
29
|
+
return config unless config[:database]
|
30
|
+
|
31
|
+
root = config[:root]
|
32
|
+
|
33
|
+
raw_scheme = config[:adapter]
|
34
|
+
database = config[:database]
|
35
|
+
password = config.fetch(:password) { '' }
|
36
|
+
username = config[:username]
|
37
|
+
hostname = config.fetch(:hostname) { 'localhost' }
|
38
|
+
|
39
|
+
adapter = Adapter[raw_scheme]
|
40
|
+
scheme = adapter.normalize_scheme(raw_scheme)
|
41
|
+
|
42
|
+
path =
|
43
|
+
if adapter.database_file?(scheme)
|
44
|
+
[root, database].compact.join('/')
|
45
|
+
else
|
46
|
+
db_path = [hostname, database].join('/')
|
47
|
+
|
48
|
+
if username && password
|
49
|
+
[[username, password].join(':'), db_path].join('@')
|
50
|
+
else
|
51
|
+
db_path
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
other_keys = config.keys - BASE_OPTIONS
|
56
|
+
options = Hash[other_keys.zip(config.values_at(*other_keys))]
|
57
|
+
|
58
|
+
config_hash("#{scheme}://#{path}", options)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @api private
|
62
|
+
def self.config_hash(uri, options = {})
|
63
|
+
if options.any?
|
64
|
+
{ default: { uri: uri, options: options } }
|
65
|
+
else
|
66
|
+
{ default: uri }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/rom/env.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module ROM
|
2
|
-
|
3
2
|
# Exposes defined repositories, schema, relations and mappers
|
4
3
|
#
|
5
4
|
# @api public
|
6
5
|
class Env
|
7
6
|
include Adamantium::Flat
|
8
|
-
include Equalizer.new(
|
7
|
+
include Equalizer.new(
|
8
|
+
:repositories, :schema, :relations, :mappers, :commands
|
9
|
+
)
|
9
10
|
|
10
11
|
attr_reader :repositories, :schema, :relations, :mappers, :commands
|
11
12
|
|
@@ -40,6 +41,10 @@ module ROM
|
|
40
41
|
commands[name]
|
41
42
|
end
|
42
43
|
|
44
|
+
# Return repository identified by its name
|
45
|
+
#
|
46
|
+
# @return [Repository]
|
47
|
+
#
|
43
48
|
# @api private
|
44
49
|
def [](name)
|
45
50
|
repositories.fetch(name)
|
@@ -52,6 +57,10 @@ module ROM
|
|
52
57
|
|
53
58
|
private
|
54
59
|
|
60
|
+
# Return repository if the method matches repository name
|
61
|
+
#
|
62
|
+
# @return [Repository]
|
63
|
+
#
|
55
64
|
# @api private
|
56
65
|
def method_missing(name, *args, &block)
|
57
66
|
if repositories.key?(name)
|
@@ -61,5 +70,4 @@ module ROM
|
|
61
70
|
end
|
62
71
|
end
|
63
72
|
end
|
64
|
-
|
65
73
|
end
|
data/lib/rom/global.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
module ROM
|
2
|
+
# Globally accessible public interface exposed via ROM module
|
3
|
+
#
|
4
|
+
# @public
|
5
|
+
module Global
|
6
|
+
# Starts the setup process for schema, relations, mappers and commands
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# ROM.setup('sqlite::memory')
|
11
|
+
#
|
12
|
+
# ROM.relation(:users) do
|
13
|
+
# # ...
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# ROM.mappers do
|
17
|
+
# define(:users) do
|
18
|
+
# # ...
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# ROM.commands(:users) do
|
23
|
+
# define(:create) do
|
24
|
+
# # ...
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# ROM.finalize # builds the env
|
29
|
+
# ROM.env # returns the env registry
|
30
|
+
#
|
31
|
+
# @param [Hash] options repository URIs
|
32
|
+
#
|
33
|
+
# @return [Setup] boot object
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
def setup(*args, &block)
|
37
|
+
config = Config.build(*args)
|
38
|
+
|
39
|
+
adapters = config.each_with_object({}) do |(name, uri_or_opts), hash|
|
40
|
+
uri, opts =
|
41
|
+
if uri_or_opts.is_a?(Hash)
|
42
|
+
uri_or_opts.values_at(:uri, :options)
|
43
|
+
else
|
44
|
+
[uri_or_opts, {}]
|
45
|
+
end
|
46
|
+
|
47
|
+
hash[name] = Adapter.setup(uri, opts)
|
48
|
+
end
|
49
|
+
|
50
|
+
repositories = adapters.each_with_object({}) do |(name, adapter), hash|
|
51
|
+
hash[name] = Repository.new(adapter)
|
52
|
+
end
|
53
|
+
|
54
|
+
boot = Setup.new(repositories)
|
55
|
+
|
56
|
+
if block
|
57
|
+
boot.instance_exec(&block)
|
58
|
+
boot.finalize
|
59
|
+
else
|
60
|
+
@boot = boot
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @see ROM::Setup#schema
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def schema(&block)
|
68
|
+
boot.schema(&block)
|
69
|
+
end
|
70
|
+
|
71
|
+
# @see ROM::Setup#relation
|
72
|
+
#
|
73
|
+
# @api public
|
74
|
+
def relation(*args, &block)
|
75
|
+
boot.relation(*args, &block)
|
76
|
+
end
|
77
|
+
|
78
|
+
# @api public
|
79
|
+
def commands(*args, &block)
|
80
|
+
boot.commands(*args, &block)
|
81
|
+
end
|
82
|
+
|
83
|
+
# @api public
|
84
|
+
def mappers(*args, &block)
|
85
|
+
boot.mappers(*args, &block)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @api public
|
89
|
+
def env
|
90
|
+
@env
|
91
|
+
end
|
92
|
+
|
93
|
+
# @api public
|
94
|
+
def finalize
|
95
|
+
@env = boot.finalize
|
96
|
+
@boot = nil
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# @api private
|
103
|
+
def boot
|
104
|
+
@boot
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/rom/header.rb
CHANGED
@@ -1,123 +1,156 @@
|
|
1
|
-
|
1
|
+
require 'rom/header/attribute'
|
2
2
|
|
3
|
-
|
3
|
+
module ROM
|
4
|
+
# Header provides information about data mapping of a specific relation
|
5
|
+
#
|
6
|
+
# Processors use headers to build objects that process raw relations that go
|
7
|
+
# through mappers.
|
8
|
+
#
|
9
|
+
# @private
|
4
10
|
class Header
|
5
11
|
include Enumerable
|
6
|
-
include Equalizer.new(:attributes)
|
12
|
+
include Equalizer.new(:attributes, :model)
|
7
13
|
|
14
|
+
# @api private
|
8
15
|
attr_reader :attributes
|
9
16
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
def self.[](type)
|
37
|
-
if type == Array || type == Hash
|
38
|
-
Embedded
|
39
|
-
else
|
40
|
-
self
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.coerce(input)
|
45
|
-
if input.kind_of?(self)
|
46
|
-
input
|
47
|
-
else
|
48
|
-
name = input[0]
|
49
|
-
meta = (input[1] || {}).dup
|
50
|
-
|
51
|
-
meta[:type] ||= Object
|
52
|
-
|
53
|
-
if meta.key?(:header)
|
54
|
-
meta[:header] = Header.coerce(meta[:header])
|
55
|
-
end
|
56
|
-
|
57
|
-
self[meta[:type]].new(name, meta)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def initialize(name, meta = {})
|
62
|
-
@name = name
|
63
|
-
@meta = meta
|
64
|
-
@key = meta.fetch(:from) { name }
|
65
|
-
end
|
66
|
-
|
67
|
-
def type
|
68
|
-
meta.fetch(:type)
|
69
|
-
end
|
70
|
-
|
71
|
-
def aliased?
|
72
|
-
key != name
|
73
|
-
end
|
74
|
-
|
75
|
-
def embedded?
|
76
|
-
false
|
77
|
-
end
|
78
|
-
|
79
|
-
def mapping
|
80
|
-
[key, name]
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.coerce(input)
|
85
|
-
if input.kind_of?(self)
|
17
|
+
# @return [Class] optional model associated with a header
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
attr_reader :model
|
21
|
+
|
22
|
+
# @return [Hash] attribute key/name mapping for all primitive attributes
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
attr_reader :mapping
|
26
|
+
|
27
|
+
# @return [Array] all attribute keys that are in a tuple
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
attr_reader :tuple_keys
|
31
|
+
|
32
|
+
# Coerce array with attribute definitions into a header object
|
33
|
+
#
|
34
|
+
# @param [Array<Array>] attribute name/option pairs
|
35
|
+
#
|
36
|
+
# @param [Class] optional model
|
37
|
+
#
|
38
|
+
# @return [Header]
|
39
|
+
#
|
40
|
+
# @api private
|
41
|
+
def self.coerce(input, model = nil)
|
42
|
+
if input.instance_of?(self)
|
86
43
|
input
|
87
44
|
else
|
88
45
|
attributes = input.each_with_object({}) { |pair, h|
|
89
46
|
h[pair.first] = Attribute.coerce(pair)
|
90
47
|
}
|
91
48
|
|
92
|
-
new(attributes)
|
49
|
+
new(attributes, model)
|
93
50
|
end
|
94
51
|
end
|
95
52
|
|
96
|
-
|
53
|
+
# @api private
|
54
|
+
def initialize(attributes, model = nil)
|
97
55
|
@attributes = attributes
|
56
|
+
@model = model
|
57
|
+
initialize_mapping
|
58
|
+
initialize_tuple_keys
|
98
59
|
end
|
99
60
|
|
61
|
+
# Iterate over attributes
|
62
|
+
#
|
63
|
+
# @yield [Attribute]
|
64
|
+
#
|
65
|
+
# @api private
|
100
66
|
def each(&block)
|
101
|
-
return to_enum unless block
|
102
67
|
attributes.values.each(&block)
|
103
68
|
end
|
104
69
|
|
70
|
+
# Return if there are any aliased attributes
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
def aliased?
|
74
|
+
any?(&:aliased?)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return attribute keys
|
78
|
+
#
|
79
|
+
# An attribute key corresponds to tuple attribute names
|
80
|
+
#
|
81
|
+
# @api private
|
105
82
|
def keys
|
106
83
|
attributes.keys
|
107
84
|
end
|
108
85
|
|
109
|
-
|
110
|
-
|
86
|
+
# Return attribute identified by its name
|
87
|
+
#
|
88
|
+
# @return [Attribute]
|
89
|
+
#
|
90
|
+
# @api private
|
91
|
+
def [](name)
|
92
|
+
attributes.fetch(name)
|
111
93
|
end
|
112
94
|
|
113
|
-
|
114
|
-
|
95
|
+
# Return all Group attributes
|
96
|
+
#
|
97
|
+
# @return [Array<Group>]
|
98
|
+
#
|
99
|
+
# @api private
|
100
|
+
def groups
|
101
|
+
by_type(Group)
|
115
102
|
end
|
116
103
|
|
117
|
-
|
118
|
-
|
104
|
+
# Return all Wrap attributes
|
105
|
+
#
|
106
|
+
# @return [Array<Wrap>]
|
107
|
+
#
|
108
|
+
# @api private
|
109
|
+
def wraps
|
110
|
+
by_type(Wrap)
|
119
111
|
end
|
120
112
|
|
121
|
-
|
113
|
+
# Return all primitive attributes (no Group and Wrap)
|
114
|
+
#
|
115
|
+
# @return [Array<Attribute>]
|
116
|
+
#
|
117
|
+
# @api private
|
118
|
+
def primitives
|
119
|
+
to_a - non_primitives
|
120
|
+
end
|
121
|
+
|
122
|
+
# Return all non-primitive attributes (only Group and Wrap types)
|
123
|
+
#
|
124
|
+
# @return [Array<Group,Wrap>]
|
125
|
+
#
|
126
|
+
# @api private
|
127
|
+
def non_primitives
|
128
|
+
groups + wraps
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
122
132
|
|
133
|
+
# Find all attribute matching specific attribute class (not kind)
|
134
|
+
#
|
135
|
+
# @return [Array<Attribute>]
|
136
|
+
#
|
137
|
+
# @api private
|
138
|
+
def by_type(*types)
|
139
|
+
select { |attribute| types.include?(attribute.class) }
|
140
|
+
end
|
141
|
+
|
142
|
+
# Set mapping hash from primitive attributes
|
143
|
+
#
|
144
|
+
# @api private
|
145
|
+
def initialize_mapping
|
146
|
+
@mapping = primitives.map(&:mapping).reduce(:merge) || {}
|
147
|
+
end
|
148
|
+
|
149
|
+
# Set all tuple keys from all attributes going deep into Wrap and Group too
|
150
|
+
#
|
151
|
+
# @api private
|
152
|
+
def initialize_tuple_keys
|
153
|
+
@tuple_keys = mapping.keys + non_primitives.map(&:tuple_keys).flatten
|
154
|
+
end
|
155
|
+
end
|
123
156
|
end
|