rom-core 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +603 -0
  3. data/LICENSE +20 -0
  4. data/README.md +18 -0
  5. data/lib/rom-core.rb +1 -0
  6. data/lib/rom/array_dataset.rb +44 -0
  7. data/lib/rom/association_set.rb +16 -0
  8. data/lib/rom/associations/abstract.rb +135 -0
  9. data/lib/rom/associations/definitions.rb +5 -0
  10. data/lib/rom/associations/definitions/abstract.rb +116 -0
  11. data/lib/rom/associations/definitions/many_to_many.rb +24 -0
  12. data/lib/rom/associations/definitions/many_to_one.rb +11 -0
  13. data/lib/rom/associations/definitions/one_to_many.rb +11 -0
  14. data/lib/rom/associations/definitions/one_to_one.rb +11 -0
  15. data/lib/rom/associations/definitions/one_to_one_through.rb +11 -0
  16. data/lib/rom/associations/many_to_many.rb +81 -0
  17. data/lib/rom/associations/many_to_one.rb +37 -0
  18. data/lib/rom/associations/one_to_many.rb +37 -0
  19. data/lib/rom/associations/one_to_one.rb +8 -0
  20. data/lib/rom/associations/one_to_one_through.rb +8 -0
  21. data/lib/rom/associations/through_identifier.rb +39 -0
  22. data/lib/rom/auto_curry.rb +55 -0
  23. data/lib/rom/cache.rb +46 -0
  24. data/lib/rom/command.rb +488 -0
  25. data/lib/rom/command_compiler.rb +239 -0
  26. data/lib/rom/command_proxy.rb +24 -0
  27. data/lib/rom/command_registry.rb +141 -0
  28. data/lib/rom/commands.rb +3 -0
  29. data/lib/rom/commands/class_interface.rb +270 -0
  30. data/lib/rom/commands/composite.rb +53 -0
  31. data/lib/rom/commands/create.rb +13 -0
  32. data/lib/rom/commands/delete.rb +14 -0
  33. data/lib/rom/commands/graph.rb +88 -0
  34. data/lib/rom/commands/graph/class_interface.rb +62 -0
  35. data/lib/rom/commands/graph/input_evaluator.rb +62 -0
  36. data/lib/rom/commands/lazy.rb +99 -0
  37. data/lib/rom/commands/lazy/create.rb +23 -0
  38. data/lib/rom/commands/lazy/delete.rb +27 -0
  39. data/lib/rom/commands/lazy/update.rb +34 -0
  40. data/lib/rom/commands/result.rb +96 -0
  41. data/lib/rom/commands/update.rb +14 -0
  42. data/lib/rom/configuration.rb +114 -0
  43. data/lib/rom/configuration_dsl.rb +87 -0
  44. data/lib/rom/configuration_dsl/command.rb +41 -0
  45. data/lib/rom/configuration_dsl/command_dsl.rb +35 -0
  46. data/lib/rom/configuration_dsl/relation.rb +26 -0
  47. data/lib/rom/configuration_plugin.rb +17 -0
  48. data/lib/rom/constants.rb +64 -0
  49. data/lib/rom/container.rb +147 -0
  50. data/lib/rom/core.rb +46 -0
  51. data/lib/rom/create_container.rb +60 -0
  52. data/lib/rom/data_proxy.rb +94 -0
  53. data/lib/rom/enumerable_dataset.rb +68 -0
  54. data/lib/rom/environment.rb +70 -0
  55. data/lib/rom/gateway.rb +184 -0
  56. data/lib/rom/global.rb +58 -0
  57. data/lib/rom/global/plugin_dsl.rb +47 -0
  58. data/lib/rom/initializer.rb +64 -0
  59. data/lib/rom/lint/enumerable_dataset.rb +54 -0
  60. data/lib/rom/lint/gateway.rb +120 -0
  61. data/lib/rom/lint/linter.rb +78 -0
  62. data/lib/rom/lint/spec.rb +20 -0
  63. data/lib/rom/lint/test.rb +98 -0
  64. data/lib/rom/mapper_registry.rb +24 -0
  65. data/lib/rom/memory.rb +4 -0
  66. data/lib/rom/memory/associations.rb +4 -0
  67. data/lib/rom/memory/associations/many_to_many.rb +10 -0
  68. data/lib/rom/memory/associations/many_to_one.rb +10 -0
  69. data/lib/rom/memory/associations/one_to_many.rb +10 -0
  70. data/lib/rom/memory/associations/one_to_one.rb +10 -0
  71. data/lib/rom/memory/commands.rb +56 -0
  72. data/lib/rom/memory/dataset.rb +97 -0
  73. data/lib/rom/memory/gateway.rb +64 -0
  74. data/lib/rom/memory/relation.rb +62 -0
  75. data/lib/rom/memory/schema.rb +23 -0
  76. data/lib/rom/memory/storage.rb +59 -0
  77. data/lib/rom/memory/types.rb +9 -0
  78. data/lib/rom/pipeline.rb +105 -0
  79. data/lib/rom/plugin.rb +25 -0
  80. data/lib/rom/plugin_base.rb +45 -0
  81. data/lib/rom/plugin_registry.rb +197 -0
  82. data/lib/rom/plugins/command/schema.rb +37 -0
  83. data/lib/rom/plugins/configuration/configuration_dsl.rb +21 -0
  84. data/lib/rom/plugins/relation/instrumentation.rb +51 -0
  85. data/lib/rom/plugins/relation/registry_reader.rb +44 -0
  86. data/lib/rom/plugins/schema/timestamps.rb +58 -0
  87. data/lib/rom/registry.rb +71 -0
  88. data/lib/rom/relation.rb +548 -0
  89. data/lib/rom/relation/class_interface.rb +282 -0
  90. data/lib/rom/relation/commands.rb +23 -0
  91. data/lib/rom/relation/composite.rb +46 -0
  92. data/lib/rom/relation/curried.rb +103 -0
  93. data/lib/rom/relation/graph.rb +197 -0
  94. data/lib/rom/relation/loaded.rb +127 -0
  95. data/lib/rom/relation/materializable.rb +66 -0
  96. data/lib/rom/relation/name.rb +111 -0
  97. data/lib/rom/relation/view_dsl.rb +64 -0
  98. data/lib/rom/relation/wrap.rb +83 -0
  99. data/lib/rom/relation_registry.rb +10 -0
  100. data/lib/rom/schema.rb +437 -0
  101. data/lib/rom/schema/associations_dsl.rb +195 -0
  102. data/lib/rom/schema/attribute.rb +419 -0
  103. data/lib/rom/schema/dsl.rb +164 -0
  104. data/lib/rom/schema/inferrer.rb +66 -0
  105. data/lib/rom/schema_plugin.rb +27 -0
  106. data/lib/rom/setup.rb +68 -0
  107. data/lib/rom/setup/auto_registration.rb +74 -0
  108. data/lib/rom/setup/auto_registration_strategies/base.rb +16 -0
  109. data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +63 -0
  110. data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +20 -0
  111. data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +18 -0
  112. data/lib/rom/setup/finalize.rb +103 -0
  113. data/lib/rom/setup/finalize/finalize_commands.rb +60 -0
  114. data/lib/rom/setup/finalize/finalize_mappers.rb +56 -0
  115. data/lib/rom/setup/finalize/finalize_relations.rb +135 -0
  116. data/lib/rom/support/configurable.rb +85 -0
  117. data/lib/rom/support/memoizable.rb +58 -0
  118. data/lib/rom/support/notifications.rb +103 -0
  119. data/lib/rom/transaction.rb +24 -0
  120. data/lib/rom/types.rb +26 -0
  121. data/lib/rom/version.rb +5 -0
  122. metadata +289 -0
@@ -0,0 +1,195 @@
1
+ require 'dry/core/inflector'
2
+
3
+ require 'rom/associations/definitions'
4
+
5
+ module ROM
6
+ class Schema
7
+ # Additional schema DSL for definition SQL associations
8
+ #
9
+ # This DSL is exposed in `associations do .. end` blocks in schema defintions.
10
+ #
11
+ # @api public
12
+ class AssociationsDSL < BasicObject
13
+ # @!attribute [r] source
14
+ # @return [Relation::Name] The source relation
15
+ attr_reader :source
16
+
17
+ # @!attribute [r] registry
18
+ # @return [RelationRegistry] Relations registry from a rom container
19
+ attr_reader :registry
20
+
21
+ # @api private
22
+ def initialize(source, &block)
23
+ @source = source
24
+ @registry = {}
25
+ instance_exec(&block)
26
+ end
27
+
28
+ # Establish a one-to-many association
29
+ #
30
+ # @example using relation identifier
31
+ # has_many :tasks
32
+ #
33
+ # @example with a :through option
34
+ # # this establishes many-to-many association
35
+ # has_many :tasks, through: :users_tasks
36
+ #
37
+ # @example using aliased association with a custom view
38
+ # has_many :posts, as: :published_posts, view: :published
39
+ #
40
+ # @example using custom target relation
41
+ # has_many :user_posts, relation: :posts
42
+ #
43
+ # @param [Symbol] target The target relation identifier
44
+ # @param [Hash] options A hash with additional options
45
+ #
46
+ # @return [Associations::OneToMany]
47
+ #
48
+ # @see #many_to_many
49
+ #
50
+ # @api public
51
+ def one_to_many(target, options = {})
52
+ if options[:through]
53
+ many_to_many(target, options)
54
+ else
55
+ add(::ROM::Associations::Definitions::OneToMany.new(source, target, options))
56
+ end
57
+ end
58
+ alias_method :has_many, :one_to_many
59
+
60
+ # Establish a one-to-one association
61
+ #
62
+ # @example using relation identifier
63
+ # one_to_one :addresses, as: :address
64
+ #
65
+ # @example with an intermediate join relation
66
+ # one_to_one :tasks, as: :priority_task, through: :assignments
67
+ #
68
+ # @param [Symbol] target The target relation identifier
69
+ # @param [Hash] options A hash with additional options
70
+ #
71
+ # @return [Associations::OneToOne]
72
+ #
73
+ # @see #belongs_to
74
+ #
75
+ # @api public
76
+ def one_to_one(target, options = {})
77
+ if options[:through]
78
+ one_to_one_through(target, options)
79
+ else
80
+ add(::ROM::Associations::Definitions::OneToOne.new(source, target, options))
81
+ end
82
+ end
83
+
84
+ # Establish a one-to-one association with a :through option
85
+ #
86
+ # @example
87
+ # one_to_one_through :users, as: :author, through: :users_posts
88
+ #
89
+ # @return [Associations::OneToOneThrough]
90
+ #
91
+ # @api public
92
+ def one_to_one_through(target, options = {})
93
+ add(::ROM::Associations::Definitions::OneToOneThrough.new(source, target, options))
94
+ end
95
+
96
+ # Establish a many-to-many association
97
+ #
98
+ # @example using relation identifier
99
+ # many_to_many :tasks, through: :users_tasks
100
+ #
101
+ # @param [Symbol] target The target relation identifier
102
+ # @param [Hash] options A hash with additional options
103
+ #
104
+ # @return [Associations::OneToOne]
105
+ #
106
+ # @see #one_to_many
107
+ #
108
+ # @api public
109
+ def many_to_many(target, options = {})
110
+ add(::ROM::Associations::Definitions::ManyToMany.new(source, target, options))
111
+ end
112
+
113
+ # Establish a many-to-one association
114
+ #
115
+ # @example using relation identifier
116
+ # many_to_one :users, as: :author
117
+ #
118
+ # @param [Symbol] target The target relation identifier
119
+ # @param [Hash] options A hash with additional options
120
+ #
121
+ # @return [Associations::OneToOne]
122
+ #
123
+ # @see #one_to_many
124
+ #
125
+ # @api public
126
+ def many_to_one(target, options = {})
127
+ add(::ROM::Associations::Definitions::ManyToOne.new(source, target, options))
128
+ end
129
+
130
+ # Shortcut for many_to_one which sets alias automatically
131
+ #
132
+ # @example with an alias (relation identifier is inferred via pluralization)
133
+ # belongs_to :user
134
+ #
135
+ # @example with an explicit alias
136
+ # belongs_to :users, as: :author
137
+ #
138
+ # @see #many_to_one
139
+ #
140
+ # @return [Associations::ManyToOne]
141
+ #
142
+ # @api public
143
+ def belongs_to(target, options = {})
144
+ many_to_one(dataset_name(target), { as: target }.merge(options))
145
+ end
146
+
147
+ # Shortcut for one_to_one which sets alias automatically
148
+ #
149
+ # @example with an alias (relation identifier is inferred via pluralization)
150
+ # one_to_one :address
151
+ #
152
+ # @example with an explicit alias and a custom view
153
+ # one_to_one :posts, as: :priority_post, view: :prioritized
154
+ #
155
+ # @see #one_to_one
156
+ #
157
+ # @return [Associations::ManyToOne]
158
+ #
159
+ # @api public
160
+ def has_one(target, options = {})
161
+ one_to_one(dataset_name(target), { as: target }.merge(options))
162
+ end
163
+
164
+ # Return an association set for a schema
165
+ #
166
+ # @return [AssociationSet]
167
+ #
168
+ # @api private
169
+ def call
170
+ AssociationSet.new(registry)
171
+ end
172
+
173
+ private
174
+
175
+ # @api private
176
+ def add(association)
177
+ key = association.as || association.name
178
+
179
+ if registry.key?(key)
180
+ ::Kernel.raise(
181
+ ::ArgumentError,
182
+ "association #{key.inspect} is already defined for #{source.to_sym.inspect} relation"
183
+ )
184
+ end
185
+
186
+ registry[key] = association
187
+ end
188
+
189
+ # @api private
190
+ def dataset_name(name)
191
+ ::Dry::Core::Inflector.pluralize(name).to_sym
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,419 @@
1
+ require 'dry/equalizer'
2
+
3
+ require 'rom/initializer'
4
+ require 'rom/support/memoizable'
5
+
6
+ module ROM
7
+ class Schema
8
+ # Schema attributes provide meta information about types and an API
9
+ # for additional operations. This class can be extended by adapters to provide
10
+ # database-specific features. In example rom-sql provides SQL::Attribute
11
+ # with more features like creating SQL expressions for queries.
12
+ #
13
+ # Schema attributes are accessible through canonical relation schemas and
14
+ # instance-level schemas.
15
+ #
16
+ # @api public
17
+ class Attribute
18
+ include Dry::Equalizer(:type, :options)
19
+ include Memoizable
20
+
21
+ extend Initializer
22
+
23
+ # @!attribute [r] type
24
+ # @return [Dry::Types::Definition, Dry::Types::Sum, Dry::Types::Constrained]
25
+ param :type
26
+
27
+ # @api private
28
+ def [](input)
29
+ type[input]
30
+ end
31
+
32
+ # Return true if this attribute type is a primary key
33
+ #
34
+ # @example
35
+ # class Users < ROM::Relation[:memory]
36
+ # schema do
37
+ # attribute :id, Types::Int
38
+ # attribute :name, Types::String
39
+ #
40
+ # primary_key :id
41
+ # end
42
+ # end
43
+ #
44
+ # Users.schema[:id].primary_key?
45
+ # # => true
46
+ #
47
+ # Users.schema[:name].primary_key?
48
+ # # => false
49
+ #
50
+ # @return [TrueClass,FalseClass]
51
+ #
52
+ # @api public
53
+ def primary_key?
54
+ meta[:primary_key].equal?(true)
55
+ end
56
+
57
+ # Return true if this attribute type is a foreign key
58
+ #
59
+ # @example
60
+ # class Tasks < ROM::Relation[:memory]
61
+ # schema do
62
+ # attribute :id, Types::Int
63
+ # attribute :user_id, Types.ForeignKey(:users)
64
+ # end
65
+ # end
66
+ #
67
+ # Users.schema[:user_id].foreign_key?
68
+ # # => true
69
+ #
70
+ # Users.schema[:id].foreign_key?
71
+ # # => false
72
+ #
73
+ # @return [TrueClass,FalseClass]
74
+ #
75
+ # @api public
76
+ def foreign_key?
77
+ meta[:foreign_key].equal?(true)
78
+ end
79
+
80
+ # Return true if this attribute type is a foreign key
81
+ #
82
+ # @example
83
+ # class Tasks < ROM::Relation[:memory]
84
+ # schema do
85
+ # attribute :user_id, Types::Int.meta(alias: :id)
86
+ # attribute :name, Types::String
87
+ # end
88
+ # end
89
+ #
90
+ # Users.schema[:user_id].aliased?
91
+ # # => true
92
+ #
93
+ # Users.schema[:name].aliased?
94
+ # # => false
95
+ #
96
+ # @return [TrueClass,FalseClass]
97
+ #
98
+ # @api public
99
+ def aliased?
100
+ !meta[:alias].nil?
101
+ end
102
+
103
+ # Return source relation of this attribute type
104
+ #
105
+ # @example
106
+ # class Tasks < ROM::Relation[:memory]
107
+ # schema do
108
+ # attribute :id, Types::Int
109
+ # attribute :user_id, Types.ForeignKey(:users)
110
+ # end
111
+ # end
112
+ #
113
+ # Users.schema[:id].source
114
+ # # => :tasks
115
+ #
116
+ # Users.schema[:user_id].source
117
+ # # => :tasks
118
+ #
119
+ # @return [Symbol, Relation::Name]
120
+ #
121
+ # @api public
122
+ def source
123
+ meta[:source]
124
+ end
125
+
126
+ # Return target relation of this attribute type
127
+ #
128
+ # @example
129
+ # class Tasks < ROM::Relation[:memory]
130
+ # schema do
131
+ # attribute :id, Types::Int
132
+ # attribute :user_id, Types.ForeignKey(:users)
133
+ # end
134
+ # end
135
+ #
136
+ # Users.schema[:id].target
137
+ # # => nil
138
+ #
139
+ # Users.schema[:user_id].target
140
+ # # => :users
141
+ #
142
+ # @return [NilClass, Symbol, Relation::Name]
143
+ #
144
+ # @api public
145
+ def target
146
+ meta[:target]
147
+ end
148
+
149
+ # Return the canonical name of this attribute name
150
+ #
151
+ # This *always* returns the name that is used in the datastore, even when
152
+ # an attribute is aliased
153
+ #
154
+ # @example
155
+ # class Tasks < ROM::Relation[:memory]
156
+ # schema do
157
+ # attribute :user_id, Types::Int.meta(alias: :id)
158
+ # attribute :name, Types::String
159
+ # end
160
+ # end
161
+ #
162
+ # Users.schema[:id].name
163
+ # # => :id
164
+ #
165
+ # Users.schema[:user_id].name
166
+ # # => :user_id
167
+ #
168
+ # @return [Symbol]
169
+ #
170
+ # @api public
171
+ def name
172
+ meta[:name]
173
+ end
174
+
175
+ # Return tuple key
176
+ #
177
+ # When schemas are projected with aliased attributes, we need a simple access to tuple keys
178
+ #
179
+ # @example
180
+ # class Tasks < ROM::Relation[:memory]
181
+ # schema do
182
+ # attribute :user_id, Types::Int.meta(alias: :id)
183
+ # attribute :name, Types::String
184
+ # end
185
+ # end
186
+ #
187
+ # Users.schema[:id].key
188
+ # # :id
189
+ #
190
+ # Users.schema.project(Users.schema[:id].aliased(:user_id)).key
191
+ # # :user_id
192
+ #
193
+ # @return [Symbol]
194
+ #
195
+ # @api public
196
+ def key
197
+ meta[:alias] || name
198
+ end
199
+
200
+ # Return attribute's alias
201
+ #
202
+ # @example
203
+ # class Tasks < ROM::Relation[:memory]
204
+ # schema do
205
+ # attribute :user_id, Types::Int.meta(alias: :id)
206
+ # attribute :name, Types::String
207
+ # end
208
+ # end
209
+ #
210
+ # Users.schema[:user_id].alias
211
+ # # => :user_id
212
+ #
213
+ # Users.schema[:name].alias
214
+ # # => nil
215
+ #
216
+ # @return [NilClass,Symbol]
217
+ #
218
+ # @api public
219
+ def alias
220
+ meta[:alias]
221
+ end
222
+
223
+ # Return new attribute type with provided alias
224
+ #
225
+ # @example
226
+ # class Tasks < ROM::Relation[:memory]
227
+ # schema do
228
+ # attribute :user_id, Types::Int
229
+ # attribute :name, Types::String
230
+ # end
231
+ # end
232
+ #
233
+ # aliased_user_id = Users.schema[:user_id].aliased(:id)
234
+ #
235
+ # aliased_user_id.aliased?
236
+ # # => true
237
+ #
238
+ # aliased_user_id.name
239
+ # # => :user_id
240
+ #
241
+ # aliased_user_id.alias
242
+ # # => :id
243
+ #
244
+ # @param [Symbol] name The alias
245
+ #
246
+ # @return [Schema::Attribute]
247
+ #
248
+ # @api public
249
+ def aliased(name)
250
+ meta(alias: name)
251
+ end
252
+ alias_method :as, :aliased
253
+
254
+ # Return new attribute type with an alias using provided prefix
255
+ #
256
+ # @example
257
+ # class Users < ROM::Relation[:memory]
258
+ # schema do
259
+ # attribute :id, Types::Int
260
+ # attribute :name, Types::String
261
+ # end
262
+ # end
263
+ #
264
+ # prefixed_id = Users.schema[:id].prefixed
265
+ #
266
+ # prefixed_id.aliased?
267
+ # # => true
268
+ #
269
+ # prefixed_id.name
270
+ # # => :id
271
+ #
272
+ # prefixed_id.alias
273
+ # # => :users_id
274
+ #
275
+ # prefixed_id = Users.schema[:id].prefixed(:user)
276
+ #
277
+ # prefixed_id.alias
278
+ # # => :user_id
279
+ #
280
+ # @param [Symbol] prefix The prefix (defaults to source.dataset)
281
+ #
282
+ # @return [Schema::Attribute]
283
+ #
284
+ # @api public
285
+ def prefixed(prefix = source.dataset)
286
+ aliased(:"#{prefix}_#{name}")
287
+ end
288
+
289
+ # Return if the attribute type is from a wrapped relation
290
+ #
291
+ # Wrapped attributes are used when two schemas from different relations
292
+ # are merged together. This way we can identify them easily and handle
293
+ # correctly in places like auto-mapping.
294
+ #
295
+ # @api public
296
+ def wrapped?
297
+ meta[:wrapped].equal?(true)
298
+ end
299
+
300
+ # Return attribute type wrapped for the specified relation name
301
+ #
302
+ # @param [Symbol] name The name of the source relation (defaults to source.dataset)
303
+ #
304
+ # @return [Schema::Attribute]
305
+ #
306
+ # @api public
307
+ def wrapped(name = source.dataset)
308
+ prefixed(name).meta(wrapped: true)
309
+ end
310
+
311
+ # Return attribute type with additional meta information
312
+ #
313
+ # Return meta information hash if no opts are provided
314
+ #
315
+ # @param [Hash] opts The meta options
316
+ #
317
+ # @return [Schema::Attribute]
318
+ #
319
+ # @api public
320
+ def meta(opts = nil)
321
+ if opts
322
+ self.class.new(type.meta(opts))
323
+ else
324
+ type.meta
325
+ end
326
+ end
327
+
328
+ # Return string representation of the attribute type
329
+ #
330
+ # @return [String]
331
+ #
332
+ # @api public
333
+ def inspect
334
+ %(#<#{self.class}[#{type.name}] #{meta.map { |k, v| "#{k}=#{v.inspect}" }.join(' ')}>)
335
+ end
336
+ alias_method :pretty_inspect, :inspect
337
+
338
+ # Check if the attribute type is equal to another
339
+ #
340
+ # @param [Dry::Type, Schema::Attribute]
341
+ #
342
+ # @return [TrueClass,FalseClass]
343
+ #
344
+ # @api public
345
+ def eql?(other)
346
+ other.is_a?(self.class) ? super : type.eql?(other)
347
+ end
348
+
349
+ # Return if this attribute type has additional attribute type for reading
350
+ # tuple values
351
+ #
352
+ # @return [TrueClass, FalseClass]
353
+ #
354
+ # @api private
355
+ def read?
356
+ ! meta[:read].nil?
357
+ end
358
+
359
+ # Return read type or self
360
+ #
361
+ # @return [Schema::Attribute]
362
+ #
363
+ # @api private
364
+ def to_read_type
365
+ read? ? meta[:read] : type
366
+ end
367
+
368
+ # @api private
369
+ def respond_to_missing?(name, include_private = false)
370
+ type.respond_to?(name) || super
371
+ end
372
+
373
+ # Return AST for the type
374
+ #
375
+ # @return [Array]
376
+ #
377
+ # @api public
378
+ def to_ast
379
+ [:attribute, [name, type.to_ast(meta: false), meta_ast]]
380
+ end
381
+
382
+ # Return AST for the read type
383
+ #
384
+ # @return [Array]
385
+ #
386
+ # @api public
387
+ def to_read_ast
388
+ [:attribute, [name, to_read_type.to_ast(meta: false), meta_ast]]
389
+ end
390
+
391
+ # @api private
392
+ def meta_ast
393
+ meta_keys = %i(wrapped alias primary_key)
394
+ ast = meta.select { |k, _| meta_keys.include?(k) }
395
+ ast[:source] = source.to_sym if source
396
+ ast
397
+ end
398
+
399
+ memoize :to_ast, :to_read_ast, :meta_ast
400
+
401
+ private
402
+
403
+ # @api private
404
+ def method_missing(meth, *args, &block)
405
+ if type.respond_to?(meth)
406
+ response = type.__send__(meth, *args, &block)
407
+
408
+ if response.is_a?(type.class)
409
+ self.class.new(type, options)
410
+ else
411
+ response
412
+ end
413
+ else
414
+ super
415
+ end
416
+ end
417
+ end
418
+ end
419
+ end