rom-core 4.2.1 → 5.0.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/README.md +0 -2
- data/lib/rom-core.rb +2 -0
- data/lib/rom/array_dataset.rb +2 -0
- data/lib/rom/association_set.rb +2 -0
- data/lib/rom/associations/abstract.rb +2 -0
- data/lib/rom/associations/definitions.rb +2 -0
- data/lib/rom/associations/definitions/abstract.rb +2 -0
- data/lib/rom/associations/definitions/many_to_many.rb +2 -0
- data/lib/rom/associations/definitions/many_to_one.rb +2 -0
- data/lib/rom/associations/definitions/one_to_many.rb +2 -0
- data/lib/rom/associations/definitions/one_to_one.rb +2 -0
- data/lib/rom/associations/definitions/one_to_one_through.rb +2 -0
- data/lib/rom/associations/many_to_many.rb +2 -0
- data/lib/rom/associations/many_to_one.rb +2 -0
- data/lib/rom/associations/one_to_many.rb +2 -0
- data/lib/rom/associations/one_to_one.rb +2 -0
- data/lib/rom/associations/one_to_one_through.rb +2 -0
- data/lib/rom/associations/through_identifier.rb +2 -0
- data/lib/rom/attribute.rb +58 -72
- data/lib/rom/auto_curry.rb +2 -0
- data/lib/rom/cache.rb +2 -0
- data/lib/rom/command.rb +7 -5
- data/lib/rom/command_compiler.rb +2 -0
- data/lib/rom/command_proxy.rb +2 -0
- data/lib/rom/command_registry.rb +13 -7
- data/lib/rom/commands.rb +2 -0
- data/lib/rom/commands/class_interface.rb +4 -2
- data/lib/rom/commands/composite.rb +2 -0
- data/lib/rom/commands/create.rb +2 -0
- data/lib/rom/commands/delete.rb +2 -0
- data/lib/rom/commands/graph.rb +2 -0
- data/lib/rom/commands/graph/class_interface.rb +2 -0
- data/lib/rom/commands/graph/input_evaluator.rb +2 -0
- data/lib/rom/commands/lazy.rb +2 -0
- data/lib/rom/commands/lazy/create.rb +2 -0
- data/lib/rom/commands/lazy/delete.rb +2 -0
- data/lib/rom/commands/lazy/update.rb +2 -0
- data/lib/rom/commands/update.rb +2 -0
- data/lib/rom/configuration.rb +2 -0
- data/lib/rom/configuration_dsl.rb +2 -0
- data/lib/rom/configuration_dsl/command.rb +2 -0
- data/lib/rom/configuration_dsl/command_dsl.rb +2 -0
- data/lib/rom/configuration_dsl/relation.rb +2 -0
- data/lib/rom/configuration_plugin.rb +2 -0
- data/lib/rom/constants.rb +3 -0
- data/lib/rom/container.rb +2 -0
- data/lib/rom/core.rb +4 -1
- data/lib/rom/create_container.rb +2 -0
- data/lib/rom/data_proxy.rb +2 -0
- data/lib/rom/enumerable_dataset.rb +2 -0
- data/lib/rom/environment.rb +2 -0
- data/lib/rom/gateway.rb +2 -0
- data/lib/rom/global.rb +2 -0
- data/lib/rom/global/plugin_dsl.rb +2 -0
- data/lib/rom/header.rb +198 -0
- data/lib/rom/header/attribute.rb +192 -0
- data/lib/rom/initializer.rb +2 -0
- data/lib/rom/lint/enumerable_dataset.rb +2 -0
- data/lib/rom/lint/gateway.rb +2 -0
- data/lib/rom/lint/linter.rb +2 -0
- data/lib/rom/lint/spec.rb +2 -0
- data/lib/rom/lint/test.rb +2 -0
- data/lib/rom/mapper.rb +100 -0
- data/lib/rom/mapper/attribute_dsl.rb +480 -0
- data/lib/rom/mapper/builder.rb +39 -0
- data/lib/rom/mapper/configuration_plugin.rb +28 -0
- data/lib/rom/mapper/dsl.rb +123 -0
- data/lib/rom/mapper/mapper_dsl.rb +45 -0
- data/lib/rom/mapper/model_dsl.rb +60 -0
- data/lib/rom/mapper_compiler.rb +84 -0
- data/lib/rom/mapper_registry.rb +2 -0
- data/lib/rom/memory.rb +2 -0
- data/lib/rom/memory/associations.rb +2 -0
- data/lib/rom/memory/associations/many_to_many.rb +2 -0
- data/lib/rom/memory/associations/many_to_one.rb +2 -0
- data/lib/rom/memory/associations/one_to_many.rb +2 -0
- data/lib/rom/memory/associations/one_to_one.rb +2 -0
- data/lib/rom/memory/commands.rb +2 -0
- data/lib/rom/memory/dataset.rb +2 -0
- data/lib/rom/memory/gateway.rb +2 -0
- data/lib/rom/memory/mapper_compiler.rb +2 -0
- data/lib/rom/memory/relation.rb +2 -0
- data/lib/rom/memory/schema.rb +2 -0
- data/lib/rom/memory/storage.rb +2 -0
- data/lib/rom/memory/types.rb +2 -0
- data/lib/rom/model_builder.rb +103 -0
- data/lib/rom/open_struct.rb +37 -0
- data/lib/rom/pipeline.rb +2 -0
- data/lib/rom/plugin.rb +2 -0
- data/lib/rom/plugin_base.rb +2 -0
- data/lib/rom/plugin_registry.rb +2 -0
- data/lib/rom/plugins/command/schema.rb +2 -0
- data/lib/rom/plugins/command/timestamps.rb +2 -0
- data/lib/rom/plugins/relation/instrumentation.rb +2 -0
- data/lib/rom/plugins/relation/registry_reader.rb +2 -0
- data/lib/rom/plugins/schema/timestamps.rb +8 -1
- data/lib/rom/processor.rb +30 -0
- data/lib/rom/processor/transproc.rb +417 -0
- data/lib/rom/registry.rb +2 -0
- data/lib/rom/relation.rb +4 -2
- data/lib/rom/relation/class_interface.rb +2 -0
- data/lib/rom/relation/combined.rb +2 -0
- data/lib/rom/relation/commands.rb +2 -0
- data/lib/rom/relation/composite.rb +2 -0
- data/lib/rom/relation/curried.rb +3 -1
- data/lib/rom/relation/graph.rb +2 -0
- data/lib/rom/relation/loaded.rb +2 -0
- data/lib/rom/relation/materializable.rb +2 -0
- data/lib/rom/relation/name.rb +2 -0
- data/lib/rom/relation/view_dsl.rb +2 -0
- data/lib/rom/relation/wrap.rb +2 -0
- data/lib/rom/relation_registry.rb +2 -0
- data/lib/rom/schema.rb +39 -6
- data/lib/rom/schema/associations_dsl.rb +5 -3
- data/lib/rom/schema/dsl.rb +41 -11
- data/lib/rom/schema/inferrer.rb +21 -3
- data/lib/rom/schema_plugin.rb +2 -0
- data/lib/rom/setup.rb +2 -0
- data/lib/rom/setup/auto_registration.rb +2 -0
- data/lib/rom/setup/auto_registration_strategies/base.rb +3 -1
- data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +2 -0
- data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +2 -0
- data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +2 -0
- data/lib/rom/setup/finalize.rb +2 -0
- data/lib/rom/setup/finalize/finalize_commands.rb +2 -0
- data/lib/rom/setup/finalize/finalize_mappers.rb +2 -0
- data/lib/rom/setup/finalize/finalize_relations.rb +2 -0
- data/lib/rom/struct.rb +108 -0
- data/lib/rom/struct_compiler.rb +110 -0
- data/lib/rom/support/configurable.rb +2 -0
- data/lib/rom/support/inflector.rb +2 -0
- data/lib/rom/support/memoizable.rb +2 -0
- data/lib/rom/support/notifications.rb +2 -0
- data/lib/rom/transaction.rb +2 -0
- data/lib/rom/transformer.rb +34 -0
- data/lib/rom/types.rb +10 -3
- data/lib/rom/version.rb +3 -1
- metadata +37 -21
data/lib/rom/memory/gateway.rb
CHANGED
data/lib/rom/memory/relation.rb
CHANGED
data/lib/rom/memory/schema.rb
CHANGED
data/lib/rom/memory/storage.rb
CHANGED
data/lib/rom/memory/types.rb
CHANGED
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rom/support/inflector'
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
# Model builders can be used to build model classes for mappers
|
7
|
+
#
|
8
|
+
# This is used when you define a mapper and setup a model using :name option.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # this will define User model for you
|
12
|
+
# class UserMapper < ROM::Mapper
|
13
|
+
# model name: 'User'
|
14
|
+
# attribute :id
|
15
|
+
# attribute :name
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# @private
|
19
|
+
class ModelBuilder
|
20
|
+
attr_reader :name
|
21
|
+
|
22
|
+
attr_reader :const_name, :namespace, :klass
|
23
|
+
|
24
|
+
# Return model builder subclass based on type
|
25
|
+
#
|
26
|
+
# @param [Symbol] type
|
27
|
+
#
|
28
|
+
# @return [Class]
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
def self.[](type)
|
32
|
+
case type
|
33
|
+
when :poro then PORO
|
34
|
+
else
|
35
|
+
raise ArgumentError, "#{type.inspect} is not a supported model type"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Build a model class
|
40
|
+
#
|
41
|
+
# @return [Class]
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
def self.call(*args)
|
45
|
+
new(*args).call
|
46
|
+
end
|
47
|
+
|
48
|
+
# @api private
|
49
|
+
def initialize(options = {})
|
50
|
+
@name = options[:name]
|
51
|
+
|
52
|
+
if name
|
53
|
+
parts = name.split('::')
|
54
|
+
|
55
|
+
@const_name = parts.pop
|
56
|
+
|
57
|
+
@namespace =
|
58
|
+
if parts.any?
|
59
|
+
Inflector.constantize(parts.join('::'))
|
60
|
+
else
|
61
|
+
Object
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Define a model class constant
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
def define_const
|
70
|
+
namespace.const_set(const_name, klass)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Build a model class supporting specific attributes
|
74
|
+
#
|
75
|
+
# @return [Class]
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
def call(attrs)
|
79
|
+
define_class(attrs)
|
80
|
+
define_const if const_name
|
81
|
+
@klass
|
82
|
+
end
|
83
|
+
|
84
|
+
# PORO model class builder
|
85
|
+
#
|
86
|
+
# @private
|
87
|
+
class PORO < ModelBuilder
|
88
|
+
def define_class(attrs)
|
89
|
+
@klass = Class.new
|
90
|
+
|
91
|
+
@klass.send(:attr_reader, *attrs)
|
92
|
+
|
93
|
+
@klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
94
|
+
def initialize(params)
|
95
|
+
#{attrs.map { |name| "@#{name} = params[:#{name}]" }.join("\n")}
|
96
|
+
end
|
97
|
+
RUBY
|
98
|
+
|
99
|
+
self
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
# ROM's open structs are used for relations with empty schemas.
|
5
|
+
# Such relations may exist in cases like using raw SQL strings
|
6
|
+
# where schema was not explicitly defined using `view` DSL.
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
class OpenStruct
|
10
|
+
IVAR = -> v { :"@#{v}" }
|
11
|
+
|
12
|
+
# @api private
|
13
|
+
def initialize(attributes)
|
14
|
+
attributes.each do |key, value|
|
15
|
+
instance_variable_set(IVAR[key], value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
def respond_to_missing?(meth, include_private = false)
|
21
|
+
super || instance_variables.include?(IVAR[meth])
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# @api private
|
27
|
+
def method_missing(meth, *args, &block)
|
28
|
+
ivar = IVAR[meth]
|
29
|
+
|
30
|
+
if instance_variables.include?(ivar)
|
31
|
+
instance_variable_get(ivar)
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/rom/pipeline.rb
CHANGED
data/lib/rom/plugin.rb
CHANGED
data/lib/rom/plugin_base.rb
CHANGED
data/lib/rom/plugin_registry.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ROM
|
2
4
|
module Plugins
|
3
5
|
module Schema
|
@@ -27,7 +29,12 @@ module ROM
|
|
27
29
|
def self.apply(schema, options)
|
28
30
|
type = options.fetch(:type, Types::Time)
|
29
31
|
names = options.fetch(:attributes, DEFAULT_TIMESTAMPS)
|
30
|
-
attributes = names.map
|
32
|
+
attributes = names.map do |name|
|
33
|
+
ROM::Schema.build_attribute_info(
|
34
|
+
type.meta(source: schema.name),
|
35
|
+
name: name
|
36
|
+
)
|
37
|
+
end
|
31
38
|
|
32
39
|
schema.attributes.concat(
|
33
40
|
schema.class.attributes(attributes, schema.attr_class)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rom/mapper'
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
# Abstract processor class
|
7
|
+
#
|
8
|
+
# Every ROM processor should inherit from this class
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
class Processor
|
12
|
+
# Hook used to auto-register a processor class
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
def self.inherited(processor)
|
16
|
+
Mapper.register_processor(processor)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Required interface to be implemented by descendants
|
20
|
+
#
|
21
|
+
# @return [Processor]
|
22
|
+
#
|
23
|
+
# @abstract
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
def self.build
|
27
|
+
raise NotImplementedError, "+build+ must be implemented"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,417 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'transproc/all'
|
4
|
+
|
5
|
+
require 'rom/processor'
|
6
|
+
|
7
|
+
module ROM
|
8
|
+
class Processor
|
9
|
+
# Data mapping transformer builder using Transproc
|
10
|
+
#
|
11
|
+
# This builds a transproc function that is used to map a whole relation
|
12
|
+
#
|
13
|
+
# @see https://github.com/solnic/transproc too
|
14
|
+
#
|
15
|
+
# @private
|
16
|
+
class Transproc < Processor
|
17
|
+
include ::Transproc::Composer
|
18
|
+
|
19
|
+
module Functions
|
20
|
+
extend ::Transproc::Registry
|
21
|
+
|
22
|
+
import ::Transproc::Coercions
|
23
|
+
import ::Transproc::ArrayTransformations
|
24
|
+
import ::Transproc::HashTransformations
|
25
|
+
import ::Transproc::ClassTransformations
|
26
|
+
import ::Transproc::ProcTransformations
|
27
|
+
INVALID_INJECT_UNION_VALUE = "%s attribute: block is required for :from with union value.".freeze
|
28
|
+
|
29
|
+
def self.identity(tuple)
|
30
|
+
tuple
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.get(arr, idx)
|
34
|
+
arr[idx]
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.filter_empty(arr)
|
38
|
+
arr.reject { |row| row.values.all?(&:nil?) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.inject_union_value(tuple, name, keys, coercer)
|
42
|
+
raise ROM::MapperMisconfiguredError, INVALID_INJECT_UNION_VALUE % [name] if !coercer
|
43
|
+
|
44
|
+
values = tuple.values_at(*keys)
|
45
|
+
result = coercer.call(*values)
|
46
|
+
|
47
|
+
tuple.merge(name => result)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Mapper] mapper that this processor belongs to
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
attr_reader :mapper
|
55
|
+
|
56
|
+
# @return [Header] header from a mapper
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
attr_reader :header
|
60
|
+
|
61
|
+
# @return [Class] model class from a mapper
|
62
|
+
#
|
63
|
+
# @api private
|
64
|
+
attr_reader :model
|
65
|
+
|
66
|
+
# @return [Hash] header's attribute mapping
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
attr_reader :mapping
|
70
|
+
|
71
|
+
# @return [Proc] row-processing proc
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
attr_reader :row_proc
|
75
|
+
|
76
|
+
# Build a transproc function from the header
|
77
|
+
#
|
78
|
+
# @param [ROM::Header] header
|
79
|
+
#
|
80
|
+
# @return [Transproc::Function]
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
def self.build(mapper, header)
|
84
|
+
new(mapper, header).to_transproc
|
85
|
+
end
|
86
|
+
|
87
|
+
# @api private
|
88
|
+
def initialize(mapper, header)
|
89
|
+
@mapper = mapper
|
90
|
+
@header = header
|
91
|
+
@model = header.model
|
92
|
+
@mapping = header.mapping
|
93
|
+
initialize_row_proc
|
94
|
+
end
|
95
|
+
|
96
|
+
# Coerce mapper header to a transproc data mapping function
|
97
|
+
#
|
98
|
+
# @return [Transproc::Function]
|
99
|
+
#
|
100
|
+
# @api private
|
101
|
+
def to_transproc
|
102
|
+
compose(t(:identity)) do |ops|
|
103
|
+
combined = header.combined
|
104
|
+
ops << t(:combine, combined.map(&method(:combined_args))) if combined.any?
|
105
|
+
ops << header.preprocessed.map { |attr| visit(attr, true) }
|
106
|
+
ops << t(:map_array, row_proc) if row_proc
|
107
|
+
ops << header.postprocessed.map { |attr| visit(attr, true) }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
# Visit an attribute from the header
|
114
|
+
#
|
115
|
+
# This forwards to a specialized visitor based on the attribute type
|
116
|
+
#
|
117
|
+
# @param [Header::Attribute] attribute
|
118
|
+
# @param [Array] args Allows to send `preprocess: true`
|
119
|
+
#
|
120
|
+
# @api private
|
121
|
+
def visit(attribute, *args)
|
122
|
+
type = attribute.class.name.split('::').last.downcase
|
123
|
+
send("visit_#{type}", attribute, *args)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Visit plain attribute
|
127
|
+
#
|
128
|
+
# It will call block transformation if it's used
|
129
|
+
#
|
130
|
+
# If it's a typed attribute a coercion transformation is added
|
131
|
+
#
|
132
|
+
# @param [Header::Attribute] attribute
|
133
|
+
#
|
134
|
+
# @api private
|
135
|
+
def visit_attribute(attribute)
|
136
|
+
coercer = attribute.meta[:coercer]
|
137
|
+
if attribute.union?
|
138
|
+
compose do |ops|
|
139
|
+
ops << t(:inject_union_value, attribute.name, attribute.key, coercer)
|
140
|
+
ops << t(:reject_keys, attribute.key) unless header.copy_keys
|
141
|
+
end
|
142
|
+
elsif coercer
|
143
|
+
t(:map_value, attribute.name, t(:bind, mapper, coercer))
|
144
|
+
elsif attribute.typed?
|
145
|
+
t(:map_value, attribute.name, t(:"to_#{attribute.type}"))
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Visit hash attribute
|
150
|
+
#
|
151
|
+
# @param [Header::Attribute::Hash] attribute
|
152
|
+
#
|
153
|
+
# @api private
|
154
|
+
def visit_hash(attribute)
|
155
|
+
with_row_proc(attribute) do |row_proc|
|
156
|
+
t(:map_value, attribute.name, row_proc)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Visit combined attribute
|
161
|
+
#
|
162
|
+
# @api private
|
163
|
+
def visit_combined(attribute)
|
164
|
+
op = with_row_proc(attribute) do |row_proc|
|
165
|
+
array_proc =
|
166
|
+
if attribute.type == :hash
|
167
|
+
t(:map_array, row_proc) >> t(:get, 0)
|
168
|
+
else
|
169
|
+
t(:map_array, row_proc)
|
170
|
+
end
|
171
|
+
|
172
|
+
t(:map_value, attribute.name, array_proc)
|
173
|
+
end
|
174
|
+
|
175
|
+
if op
|
176
|
+
op
|
177
|
+
elsif attribute.type == :hash
|
178
|
+
t(:map_value, attribute.name, t(:get, 0))
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Visit array attribute
|
183
|
+
#
|
184
|
+
# @param [Header::Attribute::Array] attribute
|
185
|
+
#
|
186
|
+
# @api private
|
187
|
+
def visit_array(attribute)
|
188
|
+
with_row_proc(attribute) do |row_proc|
|
189
|
+
t(:map_value, attribute.name, t(:map_array, row_proc))
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Visit wrapped hash attribute
|
194
|
+
#
|
195
|
+
# :nest transformation is added to handle wrapping
|
196
|
+
#
|
197
|
+
# @param [Header::Attribute::Wrap] attribute
|
198
|
+
#
|
199
|
+
# @api private
|
200
|
+
def visit_wrap(attribute)
|
201
|
+
name = attribute.name
|
202
|
+
keys = attribute.tuple_keys
|
203
|
+
|
204
|
+
compose do |ops|
|
205
|
+
ops << t(:nest, name, keys)
|
206
|
+
ops << visit_hash(attribute)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Visit unwrap attribute
|
211
|
+
#
|
212
|
+
# :unwrap transformation is added to handle unwrapping
|
213
|
+
#
|
214
|
+
# @param [Header::Attributes::Unwrap] attribute
|
215
|
+
#
|
216
|
+
# @api private
|
217
|
+
def visit_unwrap(attribute)
|
218
|
+
name = attribute.name
|
219
|
+
keys = attribute.pop_keys
|
220
|
+
|
221
|
+
compose do |ops|
|
222
|
+
ops << visit_hash(attribute)
|
223
|
+
ops << t(:unwrap, name, keys)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Visit group hash attribute
|
228
|
+
#
|
229
|
+
# :group transformation is added to handle grouping during preprocessing.
|
230
|
+
# Otherwise we simply use array visitor for the attribute.
|
231
|
+
#
|
232
|
+
# @param [Header::Attribute::Group] attribute
|
233
|
+
# @param [Boolean] preprocess true if we are building a relation preprocessing
|
234
|
+
# function that is applied to the whole relation
|
235
|
+
#
|
236
|
+
# @api private
|
237
|
+
def visit_group(attribute, preprocess = false)
|
238
|
+
if preprocess
|
239
|
+
name = attribute.name
|
240
|
+
header = attribute.header
|
241
|
+
keys = attribute.tuple_keys
|
242
|
+
|
243
|
+
others = header.preprocessed
|
244
|
+
|
245
|
+
compose do |ops|
|
246
|
+
ops << t(:group, name, keys)
|
247
|
+
ops << t(:map_array, t(:map_value, name, t(:filter_empty)))
|
248
|
+
ops << others.map { |attr|
|
249
|
+
t(:map_array, t(:map_value, name, visit(attr, true)))
|
250
|
+
}
|
251
|
+
end
|
252
|
+
else
|
253
|
+
visit_array(attribute)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
# Visit ungroup attribute
|
258
|
+
#
|
259
|
+
# :ungroup transforation is added to handle ungrouping during preprocessing.
|
260
|
+
# Otherwise we simply use array visitor for the attribute.
|
261
|
+
#
|
262
|
+
# @param [Header::Attribute::Ungroup] attribute
|
263
|
+
# @param [Boolean] preprocess true if we are building a relation preprocessing
|
264
|
+
# function that is applied to the whole relation
|
265
|
+
#
|
266
|
+
# @api private
|
267
|
+
def visit_ungroup(attribute, preprocess = false)
|
268
|
+
if preprocess
|
269
|
+
name = attribute.name
|
270
|
+
header = attribute.header
|
271
|
+
keys = attribute.pop_keys
|
272
|
+
|
273
|
+
others = header.postprocessed
|
274
|
+
|
275
|
+
compose do |ops|
|
276
|
+
ops << others.map { |attr|
|
277
|
+
t(:map_array, t(:map_value, name, visit(attr, true)))
|
278
|
+
}
|
279
|
+
ops << t(:ungroup, name, keys)
|
280
|
+
end
|
281
|
+
else
|
282
|
+
visit_array(attribute)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# Visit fold hash attribute
|
287
|
+
#
|
288
|
+
# :fold transformation is added to handle folding during preprocessing.
|
289
|
+
#
|
290
|
+
# @param [Header::Attribute::Fold] attribute
|
291
|
+
# @param [Boolean] preprocess true if we are building a relation preprocessing
|
292
|
+
# function that is applied to the whole relation
|
293
|
+
#
|
294
|
+
# @api private
|
295
|
+
def visit_fold(attribute, preprocess = false)
|
296
|
+
if preprocess
|
297
|
+
name = attribute.name
|
298
|
+
keys = attribute.tuple_keys
|
299
|
+
|
300
|
+
compose do |ops|
|
301
|
+
ops << t(:group, name, keys)
|
302
|
+
ops << t(:map_array, t(:map_value, name, t(:filter_empty)))
|
303
|
+
ops << t(:map_array, t(:fold, name, keys.first))
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
# Visit unfold hash attribute
|
309
|
+
#
|
310
|
+
# :unfold transformation is added to handle unfolding during preprocessing.
|
311
|
+
#
|
312
|
+
# @param [Header::Attribute::Unfold] attribute
|
313
|
+
# @param [Boolean] preprocess true if we are building a relation preprocessing
|
314
|
+
# function that is applied to the whole relation
|
315
|
+
#
|
316
|
+
# @api private
|
317
|
+
def visit_unfold(attribute, preprocess = false)
|
318
|
+
if preprocess
|
319
|
+
name = attribute.name
|
320
|
+
header = attribute.header
|
321
|
+
keys = attribute.pop_keys
|
322
|
+
key = keys.first
|
323
|
+
|
324
|
+
others = header.postprocessed
|
325
|
+
|
326
|
+
compose do |ops|
|
327
|
+
ops << others.map { |attr|
|
328
|
+
t(:map_array, t(:map_value, name, visit(attr, true)))
|
329
|
+
}
|
330
|
+
ops << t(:map_array, t(:map_value, name, t(:insert_key, key)))
|
331
|
+
ops << t(:map_array, t(:reject_keys, [key] - [name]))
|
332
|
+
ops << t(:ungroup, name, [key])
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
# Visit excluded attribute
|
338
|
+
#
|
339
|
+
# @param [Header::Attribute::Exclude] attribute
|
340
|
+
#
|
341
|
+
# @api private
|
342
|
+
def visit_exclude(attribute)
|
343
|
+
t(:reject_keys, [attribute.name])
|
344
|
+
end
|
345
|
+
|
346
|
+
# @api private
|
347
|
+
def combined_args(attribute)
|
348
|
+
other = attribute.header.combined
|
349
|
+
|
350
|
+
if other.any?
|
351
|
+
children = other.map(&method(:combined_args))
|
352
|
+
[attribute.name, attribute.meta[:keys], children]
|
353
|
+
else
|
354
|
+
[attribute.name, attribute.meta[:keys]]
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
# Build row_proc
|
359
|
+
#
|
360
|
+
# This transproc function is applied to each row in a dataset
|
361
|
+
#
|
362
|
+
# @api private
|
363
|
+
def initialize_row_proc
|
364
|
+
@row_proc = compose { |ops|
|
365
|
+
alias_handler = header.copy_keys ? :copy_keys : :rename_keys
|
366
|
+
process_header_keys(ops)
|
367
|
+
|
368
|
+
ops << t(alias_handler, mapping) if header.aliased?
|
369
|
+
ops << header.map { |attr| visit(attr) }
|
370
|
+
ops << t(:constructor_inject, model) if model
|
371
|
+
}
|
372
|
+
end
|
373
|
+
|
374
|
+
# Process row_proc header keys
|
375
|
+
#
|
376
|
+
# @api private
|
377
|
+
def process_header_keys(ops)
|
378
|
+
if header.reject_keys
|
379
|
+
all_keys = header.tuple_keys + header.non_primitives.map(&:key)
|
380
|
+
ops << t(:accept_keys, all_keys)
|
381
|
+
end
|
382
|
+
ops
|
383
|
+
end
|
384
|
+
|
385
|
+
# Yield row proc for a given attribute if any
|
386
|
+
#
|
387
|
+
# @param [Header::Attribute] attribute
|
388
|
+
#
|
389
|
+
# @api private
|
390
|
+
def with_row_proc(attribute)
|
391
|
+
row_proc = row_proc_from(attribute)
|
392
|
+
yield(row_proc) if row_proc
|
393
|
+
end
|
394
|
+
|
395
|
+
# Build a row_proc from a given attribute
|
396
|
+
#
|
397
|
+
# This is used by embedded attribute visitors
|
398
|
+
#
|
399
|
+
# @api private
|
400
|
+
def row_proc_from(attribute)
|
401
|
+
new(mapper, attribute.header).row_proc
|
402
|
+
end
|
403
|
+
|
404
|
+
# Return a new instance of the processor
|
405
|
+
#
|
406
|
+
# @api private
|
407
|
+
def new(*args)
|
408
|
+
self.class.new(*args)
|
409
|
+
end
|
410
|
+
|
411
|
+
# @api private
|
412
|
+
def t(*args)
|
413
|
+
Functions[*args]
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|