rom 2.0.2 → 3.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/.travis.yml +4 -7
- data/.yardopts +2 -0
- data/CHANGELOG.md +35 -1
- data/Gemfile +17 -2
- data/Rakefile +7 -2
- data/lib/rom/array_dataset.rb +44 -0
- data/lib/rom/association_set.rb +11 -5
- data/lib/rom/auto_curry.rb +55 -0
- data/lib/rom/command.rb +331 -47
- data/lib/rom/command_registry.rb +7 -18
- data/lib/rom/commands/class_interface.rb +120 -6
- data/lib/rom/commands/composite.rb +0 -1
- data/lib/rom/commands/graph.rb +7 -15
- data/lib/rom/commands/lazy/update.rb +1 -1
- data/lib/rom/configuration.rb +2 -0
- data/lib/rom/configuration_dsl/command.rb +6 -8
- data/lib/rom/configuration_dsl/mapper.rb +2 -3
- data/lib/rom/configuration_dsl/mapper_dsl.rb +0 -1
- data/lib/rom/configuration_dsl/relation.rb +4 -4
- data/lib/rom/configuration_dsl.rb +0 -4
- data/lib/rom/constants.rb +7 -1
- data/lib/rom/container.rb +11 -17
- data/lib/rom/create_container.rb +0 -2
- data/lib/rom/data_proxy.rb +94 -0
- data/lib/rom/enumerable_dataset.rb +68 -0
- data/lib/rom/gateway.rb +74 -32
- data/lib/rom/global/plugin_dsl.rb +0 -2
- data/lib/rom/global.rb +0 -2
- data/lib/rom/initializer.rb +26 -0
- data/lib/rom/lint/gateway.rb +17 -0
- data/lib/rom/mapper_registry.rb +1 -1
- data/lib/rom/memory/commands.rb +0 -2
- data/lib/rom/memory/dataset.rb +1 -2
- data/lib/rom/memory/relation.rb +14 -1
- data/lib/rom/memory/schema.rb +13 -0
- data/lib/rom/plugin_registry.rb +1 -1
- data/lib/rom/plugins/command/schema.rb +2 -2
- data/lib/rom/plugins/configuration/configuration_dsl.rb +6 -2
- data/lib/rom/plugins/relation/key_inference.rb +4 -2
- data/lib/rom/plugins/relation/registry_reader.rb +5 -1
- data/lib/rom/registry.rb +50 -0
- data/lib/rom/relation/class_interface.rb +142 -30
- data/lib/rom/relation/curried.rb +15 -15
- data/lib/rom/relation/view_dsl.rb +31 -0
- data/lib/rom/relation.rb +101 -41
- data/lib/rom/schema/attribute.rb +367 -0
- data/lib/rom/schema/dsl.rb +14 -10
- data/lib/rom/schema.rb +337 -19
- data/lib/rom/setup/auto_registration.rb +20 -17
- data/lib/rom/setup/auto_registration_strategies/base.rb +8 -3
- data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +4 -3
- data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +5 -4
- data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +3 -3
- data/lib/rom/setup/finalize/finalize_commands.rb +1 -1
- data/lib/rom/setup/finalize/finalize_mappers.rb +1 -1
- data/lib/rom/setup/finalize/finalize_relations.rb +4 -2
- data/lib/rom/setup/finalize.rb +1 -1
- data/lib/rom/transaction.rb +24 -0
- data/lib/rom/types.rb +9 -1
- data/lib/rom/version.rb +1 -1
- data/lib/rom.rb +4 -8
- data/rom.gemspec +5 -4
- data/spec/integration/command_registry_spec.rb +1 -14
- data/spec/integration/commands/create_spec.rb +5 -25
- data/spec/integration/commands/delete_spec.rb +1 -1
- data/spec/integration/commands/error_handling_spec.rb +1 -1
- data/spec/integration/commands/graph_spec.rb +20 -14
- data/spec/integration/commands/update_spec.rb +4 -27
- data/spec/integration/commands_spec.rb +1 -1
- data/spec/integration/{repositories → gateways}/extending_relations_spec.rb +1 -1
- data/spec/integration/{repositories → gateways}/setting_logger_spec.rb +2 -2
- data/spec/integration/mappers/combine_spec.rb +1 -1
- data/spec/integration/mappers/deep_embedded_spec.rb +1 -1
- data/spec/integration/mappers/definition_dsl_spec.rb +1 -1
- data/spec/integration/mappers/embedded_spec.rb +1 -1
- data/spec/integration/mappers/exclude_spec.rb +1 -1
- data/spec/integration/mappers/fold_spec.rb +1 -1
- data/spec/integration/mappers/group_spec.rb +1 -1
- data/spec/integration/mappers/overwrite_attributes_value_spec.rb +1 -1
- data/spec/integration/mappers/prefix_separator_spec.rb +1 -1
- data/spec/integration/mappers/prefix_spec.rb +1 -1
- data/spec/integration/mappers/prefixing_attributes_spec.rb +1 -1
- data/spec/integration/mappers/registering_custom_mappers_spec.rb +1 -1
- data/spec/integration/mappers/renaming_attributes_spec.rb +1 -1
- data/spec/integration/mappers/reusing_mappers_spec.rb +1 -1
- data/spec/integration/mappers/step_spec.rb +1 -1
- data/spec/integration/mappers/symbolizing_attributes_spec.rb +1 -1
- data/spec/integration/mappers/unfold_spec.rb +1 -1
- data/spec/integration/mappers/ungroup_spec.rb +2 -2
- data/spec/integration/mappers/unwrap_spec.rb +2 -2
- data/spec/integration/mappers/wrap_spec.rb +1 -1
- data/spec/integration/memory/commands/create_spec.rb +1 -1
- data/spec/integration/memory/commands/delete_spec.rb +1 -1
- data/spec/integration/memory/commands/update_spec.rb +1 -1
- data/spec/integration/multi_env_spec.rb +1 -1
- data/spec/integration/multi_repo_spec.rb +1 -1
- data/spec/integration/relations/default_dataset_spec.rb +1 -1
- data/spec/integration/relations/reading_spec.rb +1 -1
- data/spec/integration/relations/registry_dsl_spec.rb +1 -1
- data/spec/integration/setup_spec.rb +10 -4
- data/spec/shared/command_graph.rb +8 -4
- data/spec/shared/enumerable_dataset.rb +1 -1
- data/spec/spec_helper.rb +7 -9
- data/spec/support/schema.rb +14 -0
- data/spec/unit/rom/array_dataset_spec.rb +59 -0
- data/spec/unit/rom/association_set_spec.rb +4 -0
- data/spec/unit/rom/auto_curry_spec.rb +63 -0
- data/spec/unit/rom/commands/graph_spec.rb +12 -11
- data/spec/unit/rom/commands/lazy_spec.rb +8 -5
- data/spec/unit/rom/commands/pre_and_post_processors_spec.rb +336 -0
- data/spec/unit/rom/commands/result_spec.rb +1 -1
- data/spec/unit/rom/commands_spec.rb +26 -3
- data/spec/unit/rom/configuration_spec.rb +1 -1
- data/spec/unit/rom/container_spec.rb +15 -5
- data/spec/unit/rom/create_container_spec.rb +1 -1
- data/spec/unit/rom/enumerable_dataset_spec.rb +15 -0
- data/spec/unit/rom/gateway_spec.rb +1 -1
- data/spec/unit/rom/mapper_registry_spec.rb +1 -1
- data/spec/unit/rom/memory/commands_spec.rb +1 -1
- data/spec/unit/rom/memory/dataset_spec.rb +1 -1
- data/spec/unit/rom/memory/{repository_spec.rb → gateway_spec.rb} +1 -1
- data/spec/unit/rom/memory/inheritance_spec.rb +32 -0
- data/spec/unit/rom/memory/relation_spec.rb +15 -3
- data/spec/unit/rom/memory/storage_spec.rb +1 -1
- data/spec/unit/rom/plugin_spec.rb +1 -1
- data/spec/unit/rom/plugins/command/schema_spec.rb +5 -5
- data/spec/unit/rom/plugins/relation/key_inference_spec.rb +1 -1
- data/spec/unit/rom/registry_spec.rb +86 -0
- data/spec/unit/rom/relation/attribute_reader_spec.rb +17 -0
- data/spec/unit/rom/relation/call_spec.rb +51 -0
- data/spec/unit/rom/relation/composite_spec.rb +1 -1
- data/spec/unit/rom/relation/graph_spec.rb +1 -1
- data/spec/unit/rom/relation/lazy/combine_spec.rb +1 -1
- data/spec/unit/rom/relation/lazy_spec.rb +1 -1
- data/spec/unit/rom/relation/loaded_spec.rb +1 -1
- data/spec/unit/rom/relation/schema_spec.rb +50 -6
- data/spec/unit/rom/relation/view_spec.rb +122 -0
- data/spec/unit/rom/relation_spec.rb +20 -5
- data/spec/unit/rom/schema/accessing_attributes_spec.rb +52 -0
- data/spec/unit/rom/schema/append_spec.rb +17 -0
- data/spec/unit/rom/schema/exclude_spec.rb +15 -0
- data/spec/unit/rom/schema/finalize_spec.rb +59 -0
- data/spec/unit/rom/schema/key_predicate_spec.rb +15 -0
- data/spec/unit/rom/schema/merge_spec.rb +17 -0
- data/spec/unit/rom/schema/prefix_spec.rb +16 -0
- data/spec/unit/rom/schema/project_spec.rb +15 -0
- data/spec/unit/rom/schema/rename_spec.rb +22 -0
- data/spec/unit/rom/schema/type_spec.rb +49 -0
- data/spec/unit/rom/schema/uniq_spec.rb +21 -0
- data/spec/unit/rom/schema/wrap_spec.rb +17 -0
- data/spec/unit/rom/schema_spec.rb +2 -2
- metadata +79 -17
- data/lib/rom/plugins/relation/view/dsl.rb +0 -32
- data/lib/rom/plugins/relation/view.rb +0 -95
- data/spec/unit/rom/plugins/relation/view_spec.rb +0 -51
data/lib/rom/relation.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'rom/initializer'
|
1
2
|
require 'rom/relation/class_interface'
|
2
3
|
|
3
4
|
require 'rom/pipeline'
|
@@ -17,82 +18,97 @@ module ROM
|
|
17
18
|
# Base relation class
|
18
19
|
#
|
19
20
|
# Relation is a proxy for the dataset object provided by the gateway. It
|
20
|
-
#
|
21
|
+
# can forward methods to the dataset, which is why the "native" interface of
|
21
22
|
# the underlying gateway is available in the relation. This interface,
|
22
23
|
# however, is considered private and should not be used outside of the
|
23
24
|
# relation instance.
|
24
25
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
26
|
+
# Individual adapters sets up their relation classes and provide different APIs
|
27
|
+
# depending on their persistence backend.
|
28
|
+
#
|
29
|
+
# Vanilla Relation class doesn't have APIs that are specific to ROM container setup.
|
30
|
+
# When adapter Relation class inherits from this class, these APIs are added automatically,
|
31
|
+
# so that they can be registered within a container.
|
32
|
+
#
|
33
|
+
# @see ROM::Relation::ClassInterface
|
29
34
|
#
|
30
35
|
# @api public
|
31
36
|
class Relation
|
37
|
+
# Default no-op output schema which is called in `Relation#each`
|
38
|
+
NOOP_OUTPUT_SCHEMA = -> tuple { tuple }.freeze
|
39
|
+
|
40
|
+
extend Initializer
|
32
41
|
extend ClassInterface
|
33
42
|
|
34
|
-
include Options
|
35
43
|
include Dry::Equalizer(:dataset)
|
36
44
|
include Materializable
|
37
45
|
include Pipeline
|
38
46
|
|
47
|
+
# @!attribute [r] dataset
|
48
|
+
# @return [Object] dataset used by the relation provided by relation's gateway
|
49
|
+
# @api public
|
50
|
+
param :dataset
|
51
|
+
|
39
52
|
# @!attribute [r] mappers
|
40
53
|
# @return [MapperRegistry] an optional mapper registry (empty by default)
|
41
54
|
option :mappers, reader: true, default: proc { MapperRegistry.new }
|
42
55
|
|
43
|
-
# @!attribute [r]
|
56
|
+
# @!attribute [r] schema
|
57
|
+
# @return [Schema] relation schema, defaults to class-level canonical
|
58
|
+
# schema (if it was defined) and sets an empty one as
|
59
|
+
# the fallback
|
60
|
+
# @api public
|
61
|
+
option :schema, reader: true, optional: true, default: method(:default_schema).to_proc
|
62
|
+
|
63
|
+
# @!attribute [r] input_schema
|
44
64
|
# @return [Object#[]] tuple processing function, uses schema or defaults to Hash[]
|
45
65
|
# @api private
|
46
|
-
option :
|
47
|
-
relation.schema? ?
|
66
|
+
option :input_schema, reader: true, default: -> relation {
|
67
|
+
relation.schema? ? schema.to_input_hash : Hash
|
48
68
|
}
|
49
69
|
|
50
|
-
# @!attribute [r]
|
51
|
-
# @return [
|
52
|
-
|
53
|
-
|
70
|
+
# @!attribute [r] output_schema
|
71
|
+
# @return [Object#[]] tuple processing function, uses schema or defaults to NOOP_OUTPUT_SCHEMA
|
72
|
+
# @api private
|
73
|
+
option :output_schema, reader: true, optional: true, default: -> relation {
|
74
|
+
relation.schema.any?(&:read?) ? schema.to_output_hash : NOOP_OUTPUT_SCHEMA
|
54
75
|
}
|
55
76
|
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
# @!attribute [r] schema
|
62
|
-
# @return [Schema] returns relation schema object (if defined)
|
63
|
-
# @api public
|
64
|
-
attr_reader :schema
|
65
|
-
|
66
|
-
# Initializes a relation object
|
77
|
+
# Return schema attribute
|
78
|
+
#
|
79
|
+
# @example accessing canonical attribute
|
80
|
+
# users[:id]
|
81
|
+
# # => #<ROM::SQL::Attribute[Integer] primary_key=true name=:id source=ROM::Relation::Name(users)>
|
67
82
|
#
|
68
|
-
# @
|
83
|
+
# @example accessing joined attribute
|
84
|
+
# tasks_with_users = tasks.join(users).select_append(tasks[:title])
|
85
|
+
# tasks_with_users[:title, :tasks]
|
86
|
+
# # => #<ROM::SQL::Attribute[String] primary_key=false name=:title source=ROM::Relation::Name(tasks)>
|
69
87
|
#
|
70
|
-
# @
|
71
|
-
# @option :mappers [MapperRegistry]
|
72
|
-
# @option :schema_hash [#[]]
|
73
|
-
# @option :associations [AssociationSet]
|
88
|
+
# @return [Schema::Attribute]
|
74
89
|
#
|
75
90
|
# @api public
|
76
|
-
def
|
77
|
-
|
78
|
-
@schema = self.class.schema
|
79
|
-
super
|
91
|
+
def [](name)
|
92
|
+
schema[name]
|
80
93
|
end
|
81
94
|
|
82
95
|
# Yields relation tuples
|
83
96
|
#
|
97
|
+
# Every tuple is processed through Relation#output_schema, it's a no-op by default
|
98
|
+
#
|
84
99
|
# @yield [Hash]
|
100
|
+
#
|
85
101
|
# @return [Enumerator] if block is not provided
|
86
102
|
#
|
87
103
|
# @api public
|
88
104
|
def each(&block)
|
89
105
|
return to_enum unless block
|
90
|
-
dataset.each { |tuple| yield(tuple) }
|
106
|
+
dataset.each { |tuple| yield(output_schema[tuple]) }
|
91
107
|
end
|
92
108
|
|
93
109
|
# Composes with other relations
|
94
110
|
#
|
95
|
-
# @param
|
111
|
+
# @param [Array<Relation>] others The other relation(s) to compose with
|
96
112
|
#
|
97
113
|
# @return [Relation::Graph]
|
98
114
|
#
|
@@ -143,27 +159,71 @@ module ROM
|
|
143
159
|
#
|
144
160
|
# @api private
|
145
161
|
def schema?
|
146
|
-
! schema.
|
162
|
+
! schema.empty?
|
163
|
+
end
|
164
|
+
|
165
|
+
# Return a new relation with provided dataset and additional options
|
166
|
+
#
|
167
|
+
# Use this method whenever you need to use dataset API to get a new dataset
|
168
|
+
# and you want to return a relation back. Typically relation API should be
|
169
|
+
# enough though. If you find yourself using this method, it might be worth
|
170
|
+
# to consider reporting an issue that some dataset functionality is not available
|
171
|
+
# through relation API.
|
172
|
+
#
|
173
|
+
# @example with a new dataset
|
174
|
+
# users.new(users.dataset.some_method)
|
175
|
+
#
|
176
|
+
# @example with a new dataset and options
|
177
|
+
# users.new(users.dataset.some_method, other: 'options')
|
178
|
+
#
|
179
|
+
# @param [Object] dataset
|
180
|
+
# @param [Hash] new_opts Additional options
|
181
|
+
#
|
182
|
+
# @api public
|
183
|
+
def new(dataset, new_opts = EMPTY_HASH)
|
184
|
+
self.class.new(dataset, new_opts.empty? ? options : options.merge(new_opts))
|
147
185
|
end
|
148
186
|
|
149
187
|
# Returns a new instance with the same dataset but new options
|
150
188
|
#
|
189
|
+
# @example
|
190
|
+
# users.with(output_schema: -> tuple { .. })
|
191
|
+
#
|
151
192
|
# @param new_options [Hash]
|
152
193
|
#
|
153
194
|
# @return [Relation]
|
154
195
|
#
|
155
196
|
# @api private
|
156
197
|
def with(new_options)
|
157
|
-
|
198
|
+
new(dataset, options.merge(new_options))
|
158
199
|
end
|
159
200
|
|
160
|
-
|
201
|
+
# Return all registered relation schemas
|
202
|
+
#
|
203
|
+
# This holds all schemas defined via `view` DSL
|
204
|
+
#
|
205
|
+
# @return [Hash<Symbol=>Schema>]
|
206
|
+
#
|
207
|
+
# @api public
|
208
|
+
def schemas
|
209
|
+
@schemas ||= self.class.schemas
|
210
|
+
end
|
161
211
|
|
162
|
-
#
|
163
|
-
|
164
|
-
|
212
|
+
# Return schema's association set (empty by default)
|
213
|
+
#
|
214
|
+
# @return [AssociationSet] Schema's association set (empty by default)
|
215
|
+
#
|
216
|
+
# @api public
|
217
|
+
def associations
|
218
|
+
@associations ||= schema.associations
|
165
219
|
end
|
166
220
|
|
221
|
+
private
|
222
|
+
|
223
|
+
# Hook used by `Pipeline` to get the class that should be used for composition
|
224
|
+
#
|
225
|
+
# @return [Class]
|
226
|
+
#
|
167
227
|
# @api private
|
168
228
|
def composite_class
|
169
229
|
Relation::Composite
|
@@ -0,0 +1,367 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'dry/equalizer'
|
3
|
+
require 'dry/types/decorator'
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
class Schema
|
7
|
+
# Schema attributes provide meta information about types and an API
|
8
|
+
# for additional operations. This class can be extended by adapters to provide
|
9
|
+
# database-specific features. In example rom-sql provides SQL::Attribute
|
10
|
+
# with more features like creating SQL expressions for queries.
|
11
|
+
#
|
12
|
+
# Schema attributes are accessible through canonical relation schemas and
|
13
|
+
# instance-level schemas.
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
class Attribute
|
17
|
+
include Dry::Equalizer(:type)
|
18
|
+
|
19
|
+
# !@attribute [r] type
|
20
|
+
# @return [Dry::Types::Definition, Dry::Types::Sum, Dry::Types::Constrained]
|
21
|
+
attr_reader :type
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
def initialize(type)
|
25
|
+
@type = type
|
26
|
+
end
|
27
|
+
|
28
|
+
# @api private
|
29
|
+
def [](input)
|
30
|
+
type[input]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return true if this attribute type is a primary key
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# class Users < ROM::Relation[:memory]
|
37
|
+
# schema do
|
38
|
+
# attribute :id, Types::Int
|
39
|
+
# attribute :name, Types::String
|
40
|
+
#
|
41
|
+
# primary_key :id
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# Users.schema[:id].primary_key?
|
46
|
+
# # => true
|
47
|
+
#
|
48
|
+
# Users.schema[:name].primary_key?
|
49
|
+
# # => false
|
50
|
+
#
|
51
|
+
# @return [TrueClass,FalseClass]
|
52
|
+
#
|
53
|
+
# @api public
|
54
|
+
def primary_key?
|
55
|
+
meta[:primary_key].equal?(true)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Return true if this attribute type is a foreign key
|
59
|
+
#
|
60
|
+
# @example
|
61
|
+
# class Tasks < ROM::Relation[:memory]
|
62
|
+
# schema do
|
63
|
+
# attribute :id, Types::Int
|
64
|
+
# attribute :user_id, Types.ForeignKey(:users)
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# Users.schema[:user_id].foreign_key?
|
69
|
+
# # => true
|
70
|
+
#
|
71
|
+
# Users.schema[:id].foreign_key?
|
72
|
+
# # => false
|
73
|
+
#
|
74
|
+
# @return [TrueClass,FalseClass]
|
75
|
+
#
|
76
|
+
# @api public
|
77
|
+
def foreign_key?
|
78
|
+
meta[:foreign_key].equal?(true)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Return true if this attribute type is a foreign key
|
82
|
+
#
|
83
|
+
# @example
|
84
|
+
# class Tasks < ROM::Relation[:memory]
|
85
|
+
# schema do
|
86
|
+
# attribute :user_id, Types::Int.meta(alias: :id)
|
87
|
+
# attribute :name, Types::String
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# Users.schema[:user_id].aliased?
|
92
|
+
# # => true
|
93
|
+
#
|
94
|
+
# Users.schema[:name].aliased?
|
95
|
+
# # => false
|
96
|
+
#
|
97
|
+
# @return [TrueClass,FalseClass]
|
98
|
+
#
|
99
|
+
# @api public
|
100
|
+
def aliased?
|
101
|
+
!meta[:alias].nil?
|
102
|
+
end
|
103
|
+
|
104
|
+
# Return source relation of this attribute type
|
105
|
+
#
|
106
|
+
# @example
|
107
|
+
# class Tasks < ROM::Relation[:memory]
|
108
|
+
# schema do
|
109
|
+
# attribute :id, Types::Int
|
110
|
+
# attribute :user_id, Types.ForeignKey(:users)
|
111
|
+
# end
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# Users.schema[:id].source
|
115
|
+
# # => :tasks
|
116
|
+
#
|
117
|
+
# Users.schema[:user_id].source
|
118
|
+
# # => :tasks
|
119
|
+
#
|
120
|
+
# @return [Symbol, Relation::Name]
|
121
|
+
#
|
122
|
+
# @api public
|
123
|
+
def source
|
124
|
+
meta[:source]
|
125
|
+
end
|
126
|
+
|
127
|
+
# Return target relation of this attribute type
|
128
|
+
#
|
129
|
+
# @example
|
130
|
+
# class Tasks < ROM::Relation[:memory]
|
131
|
+
# schema do
|
132
|
+
# attribute :id, Types::Int
|
133
|
+
# attribute :user_id, Types.ForeignKey(:users)
|
134
|
+
# end
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# Users.schema[:id].target
|
138
|
+
# # => nil
|
139
|
+
#
|
140
|
+
# Users.schema[:user_id].target
|
141
|
+
# # => :users
|
142
|
+
#
|
143
|
+
# @return [NilClass, Symbol, Relation::Name]
|
144
|
+
#
|
145
|
+
# @api public
|
146
|
+
def target
|
147
|
+
meta[:target]
|
148
|
+
end
|
149
|
+
|
150
|
+
# Return the canonical name of this attribute name
|
151
|
+
#
|
152
|
+
# This *always* returns the name that is used in the datastore, even when
|
153
|
+
# an attribute is aliased
|
154
|
+
#
|
155
|
+
# @example
|
156
|
+
# class Tasks < ROM::Relation[:memory]
|
157
|
+
# schema do
|
158
|
+
# attribute :user_id, Types::Int.meta(alias: :id)
|
159
|
+
# attribute :name, Types::String
|
160
|
+
# end
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# Users.schema[:id].name
|
164
|
+
# # => :id
|
165
|
+
#
|
166
|
+
# Users.schema[:user_id].name
|
167
|
+
# # => :user_id
|
168
|
+
#
|
169
|
+
# @return [Symbol]
|
170
|
+
#
|
171
|
+
# @api public
|
172
|
+
def name
|
173
|
+
meta[:name]
|
174
|
+
end
|
175
|
+
|
176
|
+
# Return attribute's alias
|
177
|
+
#
|
178
|
+
# @example
|
179
|
+
# class Tasks < ROM::Relation[:memory]
|
180
|
+
# schema do
|
181
|
+
# attribute :user_id, Types::Int.meta(alias: :id)
|
182
|
+
# attribute :name, Types::String
|
183
|
+
# end
|
184
|
+
# end
|
185
|
+
#
|
186
|
+
# Users.schema[:user_id].alias
|
187
|
+
# # => :user_id
|
188
|
+
#
|
189
|
+
# Users.schema[:name].alias
|
190
|
+
# # => nil
|
191
|
+
#
|
192
|
+
# @return [NilClass,Symbol]
|
193
|
+
#
|
194
|
+
# @api public
|
195
|
+
def alias
|
196
|
+
meta[:alias]
|
197
|
+
end
|
198
|
+
|
199
|
+
# Return new attribute type with provided alias
|
200
|
+
#
|
201
|
+
# @example
|
202
|
+
# class Tasks < ROM::Relation[:memory]
|
203
|
+
# schema do
|
204
|
+
# attribute :user_id, Types::Int
|
205
|
+
# attribute :name, Types::String
|
206
|
+
# end
|
207
|
+
# end
|
208
|
+
#
|
209
|
+
# aliased_user_id = Users.schema[:user_id].aliased(:id)
|
210
|
+
#
|
211
|
+
# aliased_user_id.aliased?
|
212
|
+
# # => true
|
213
|
+
#
|
214
|
+
# aliased_user_id.name
|
215
|
+
# # => :user_id
|
216
|
+
#
|
217
|
+
# aliased_user_id.alias
|
218
|
+
# # => :id
|
219
|
+
#
|
220
|
+
# @param [Symbol] name The alias
|
221
|
+
#
|
222
|
+
# @return [Schema::Attribute]
|
223
|
+
#
|
224
|
+
# @api public
|
225
|
+
def aliased(name)
|
226
|
+
meta(alias: name)
|
227
|
+
end
|
228
|
+
alias_method :as, :aliased
|
229
|
+
|
230
|
+
# Return new attribute type with an alias using provided prefix
|
231
|
+
#
|
232
|
+
# @example
|
233
|
+
# class Users < ROM::Relation[:memory]
|
234
|
+
# schema do
|
235
|
+
# attribute :id, Types::Int
|
236
|
+
# attribute :name, Types::String
|
237
|
+
# end
|
238
|
+
# end
|
239
|
+
#
|
240
|
+
# prefixed_id = Users.schema[:id].prefixed
|
241
|
+
#
|
242
|
+
# prefixed_id.aliased?
|
243
|
+
# # => true
|
244
|
+
#
|
245
|
+
# prefixed_id.name
|
246
|
+
# # => :id
|
247
|
+
#
|
248
|
+
# prefixed_id.alias
|
249
|
+
# # => :users_id
|
250
|
+
#
|
251
|
+
# prefixed_id = Users.schema[:id].prefixed(:user)
|
252
|
+
#
|
253
|
+
# prefixed_id.alias
|
254
|
+
# # => :user_id
|
255
|
+
#
|
256
|
+
# @param [Symbol] prefix The prefix (defaults to source.dataset)
|
257
|
+
#
|
258
|
+
# @return [Schema::Attribute]
|
259
|
+
#
|
260
|
+
# @api public
|
261
|
+
def prefixed(prefix = source.dataset)
|
262
|
+
aliased(:"#{prefix}_#{name}")
|
263
|
+
end
|
264
|
+
|
265
|
+
# Return if the attribute type is from a wrapped relation
|
266
|
+
#
|
267
|
+
# Wrapped attributes are used when two schemas from different relations
|
268
|
+
# are merged together. This way we can identify them easily and handle
|
269
|
+
# correctly in places like auto-mapping.
|
270
|
+
#
|
271
|
+
# @api public
|
272
|
+
def wrapped?
|
273
|
+
meta[:wrapped].equal?(true)
|
274
|
+
end
|
275
|
+
|
276
|
+
# Return attribute type wrapped for the specified relation name
|
277
|
+
#
|
278
|
+
# @param [Symbol] name The name of the source relation (defaults to source.dataset)
|
279
|
+
#
|
280
|
+
# @return [Schema::Attribute]
|
281
|
+
#
|
282
|
+
# @api public
|
283
|
+
def wrapped(name = source.dataset)
|
284
|
+
self.class.new(prefixed(name).meta(wrapped: true))
|
285
|
+
end
|
286
|
+
|
287
|
+
# Return attribute type with additional meta information
|
288
|
+
#
|
289
|
+
# Return meta information hash if no opts are provided
|
290
|
+
#
|
291
|
+
# @param [Hash] opts The meta options
|
292
|
+
#
|
293
|
+
# @return [Schema::Attribute]
|
294
|
+
#
|
295
|
+
# @api public
|
296
|
+
def meta(opts = nil)
|
297
|
+
if opts
|
298
|
+
self.class.new(type.meta(opts))
|
299
|
+
else
|
300
|
+
type.meta
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
# Return string representation of the attribute type
|
305
|
+
#
|
306
|
+
# @return [String]
|
307
|
+
#
|
308
|
+
# @api public
|
309
|
+
def inspect
|
310
|
+
%(#<#{self.class}[#{type.name}] #{meta.map { |k, v| "#{k}=#{v.inspect}" }.join(' ')}>)
|
311
|
+
end
|
312
|
+
alias_method :pretty_inspect, :inspect
|
313
|
+
|
314
|
+
# Check if the attribute type is equal to another
|
315
|
+
#
|
316
|
+
# @param [Dry::Type, Schema::Attribute]
|
317
|
+
#
|
318
|
+
# @return [TrueClass,FalseClass]
|
319
|
+
#
|
320
|
+
# @api public
|
321
|
+
def eql?(other)
|
322
|
+
other.is_a?(self.class) ? super : type.eql?(other)
|
323
|
+
end
|
324
|
+
|
325
|
+
# Return if this attribute type has additional attribute type for reading
|
326
|
+
# tuple values
|
327
|
+
#
|
328
|
+
# @return [TrueClass, FalseClass]
|
329
|
+
#
|
330
|
+
# @api private
|
331
|
+
def read?
|
332
|
+
! meta[:read].nil?
|
333
|
+
end
|
334
|
+
|
335
|
+
# Return read type or self
|
336
|
+
#
|
337
|
+
# @return [Schema::Attribute]
|
338
|
+
#
|
339
|
+
# @api private
|
340
|
+
def to_read_type
|
341
|
+
read? ? meta[:read] : type
|
342
|
+
end
|
343
|
+
|
344
|
+
# @api private
|
345
|
+
def respond_to_missing?(name, include_private = false)
|
346
|
+
type.respond_to?(name) || super
|
347
|
+
end
|
348
|
+
|
349
|
+
private
|
350
|
+
|
351
|
+
# @api private
|
352
|
+
def method_missing(meth, *args, &block)
|
353
|
+
if type.respond_to?(meth)
|
354
|
+
response = type.__send__(meth, *args, &block)
|
355
|
+
|
356
|
+
if response.is_a?(type.class)
|
357
|
+
self.class.new(type)
|
358
|
+
else
|
359
|
+
response
|
360
|
+
end
|
361
|
+
else
|
362
|
+
super
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
data/lib/rom/schema/dsl.rb
CHANGED
@@ -13,19 +13,17 @@ module ROM
|
|
13
13
|
|
14
14
|
# @api public
|
15
15
|
class DSL < BasicObject
|
16
|
-
attr_reader :
|
16
|
+
attr_reader :relation, :attributes, :inferrer, :schema_class
|
17
17
|
|
18
18
|
# @api private
|
19
|
-
def initialize(
|
20
|
-
@
|
19
|
+
def initialize(relation, schema_class: Schema, inferrer: Schema::DEFAULT_INFERRER, &block)
|
20
|
+
@relation = relation
|
21
21
|
@inferrer = inferrer
|
22
|
-
@
|
22
|
+
@schema_class = schema_class
|
23
|
+
@attributes = {}
|
23
24
|
|
24
25
|
if block
|
25
26
|
instance_exec(&block)
|
26
|
-
elsif inferrer.nil?
|
27
|
-
raise ArgumentError,
|
28
|
-
'You must pass a block to define a schema or set an inferrer for automatic inferring'
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
@@ -34,9 +32,15 @@ module ROM
|
|
34
32
|
# @see Relation.schema
|
35
33
|
#
|
36
34
|
# @api public
|
37
|
-
def attribute(name, type)
|
35
|
+
def attribute(name, type, options = EMPTY_HASH)
|
38
36
|
@attributes ||= {}
|
39
|
-
|
37
|
+
|
38
|
+
@attributes[name] =
|
39
|
+
if options[:read]
|
40
|
+
type.meta(name: name, source: relation, read: options[:read])
|
41
|
+
else
|
42
|
+
type.meta(name: name, source: relation)
|
43
|
+
end
|
40
44
|
end
|
41
45
|
|
42
46
|
# Specify which key(s) should be the primary key
|
@@ -51,7 +55,7 @@ module ROM
|
|
51
55
|
|
52
56
|
# @api private
|
53
57
|
def call
|
54
|
-
|
58
|
+
schema_class.define(relation, attributes: attributes.values, inferrer: inferrer)
|
55
59
|
end
|
56
60
|
end
|
57
61
|
end
|