-
-
Notifications
You must be signed in to change notification settings - Fork 161
/
inferrer.rb
106 lines (86 loc) · 3.07 KB
/
inferrer.rb
1
2
3
4
5
6
7
8
9
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# frozen_string_literal: true
require 'dry/core/class_attributes'
module ROM
class Schema
# @api private
class Inferrer
extend Dry::Core::ClassAttributes
extend Initializer
# @!method self.attributes_inferrer
# @overload attributes_inferrer
# @return [Proc]
#
# @overload attributes_inferrer(value)
# @param value [Proc]
# @return [Proc]
#
# @!method self.attr_class
# @overload attr_class
# @return [Class(ROM::Attribute)]
#
# @overload attr_class(value)
# @param value [Class(ROM::Attribute)]
# @return [Class(ROM::Attribute)]
defines :attributes_inferrer, :attr_class
MissingAttributesError = Class.new(StandardError) do
def initialize(name, attributes)
super(
"Following attributes in #{Relation::Name[name].relation.inspect} schema cannot "\
"be inferred and have to be defined explicitly: #{attributes.map(&:inspect).join(', ')}"
)
end
end
DEFAULT_ATTRIBUTES = [EMPTY_ARRAY, EMPTY_ARRAY].freeze
attributes_inferrer -> * { DEFAULT_ATTRIBUTES }
attr_class Attribute
include Dry::Equalizer(:options)
# @!attribute [r] attr_class
# @return [Class(ROM::Attribute)]
option :attr_class, default: -> { self.class.attr_class }
# @!attribute [r] enabled
# @return [Boolean]
option :enabled, default: -> { true }
alias_method :enabled?, :enabled
# @!attribute [r] attributes_inferrer
# @return [ROM::Schema::AttributesInferrer]
option :attributes_inferrer, default: -> { self.class.attributes_inferrer }
# @api private
def call(schema, gateway)
if enabled?
inferred, missing = attributes_inferrer.(schema, gateway, options)
else
inferred, missing = DEFAULT_ATTRIBUTES
end
attributes = merge_attributes(schema.attributes, inferred)
check_all_attributes_defined(schema, attributes, missing)
{ attributes: attributes }
end
# @api private
def check_all_attributes_defined(schema, all_known, not_inferred)
not_defined = not_inferred - all_known.map(&:name)
raise MissingAttributesError.new(schema.name, not_defined) unless not_defined.empty?
end
# @api private
def merge_attributes(defined, inferred)
type_lookup = lambda do |attrs, name|
attrs.find { |a| a.name == name }.type
end
defined_with_type, defined_names =
defined.each_with_object([[], []]) do |attr, (attrs, names)|
attrs << if attr.type.nil?
attr.class.new(
type_lookup.(inferred, attr.name),
**attr.options
)
else
attr
end
names << attr.name
end
defined_with_type + inferred.reject do |attr|
defined_names.include?(attr.name)
end
end
end
end
end