rom-core 4.0.0.beta1

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.
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