hanami-model 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/README.md +54 -420
  4. data/hanami-model.gemspec +9 -6
  5. data/lib/hanami/entity.rb +107 -191
  6. data/lib/hanami/entity/schema.rb +236 -0
  7. data/lib/hanami/model.rb +52 -138
  8. data/lib/hanami/model/association.rb +37 -0
  9. data/lib/hanami/model/associations/belongs_to.rb +19 -0
  10. data/lib/hanami/model/associations/dsl.rb +29 -0
  11. data/lib/hanami/model/associations/has_many.rb +200 -0
  12. data/lib/hanami/model/configuration.rb +52 -224
  13. data/lib/hanami/model/configurator.rb +62 -0
  14. data/lib/hanami/model/entity_name.rb +35 -0
  15. data/lib/hanami/model/error.rb +37 -24
  16. data/lib/hanami/model/mapping.rb +29 -35
  17. data/lib/hanami/model/migration.rb +31 -0
  18. data/lib/hanami/model/migrator.rb +111 -88
  19. data/lib/hanami/model/migrator/adapter.rb +39 -16
  20. data/lib/hanami/model/migrator/connection.rb +23 -11
  21. data/lib/hanami/model/migrator/mysql_adapter.rb +38 -17
  22. data/lib/hanami/model/migrator/postgres_adapter.rb +20 -19
  23. data/lib/hanami/model/migrator/sqlite_adapter.rb +9 -8
  24. data/lib/hanami/model/plugins.rb +25 -0
  25. data/lib/hanami/model/plugins/mapping.rb +55 -0
  26. data/lib/hanami/model/plugins/schema.rb +55 -0
  27. data/lib/hanami/model/plugins/timestamps.rb +118 -0
  28. data/lib/hanami/model/relation_name.rb +24 -0
  29. data/lib/hanami/model/sql.rb +161 -0
  30. data/lib/hanami/model/sql/console.rb +41 -0
  31. data/lib/hanami/model/sql/consoles/abstract.rb +33 -0
  32. data/lib/hanami/model/sql/consoles/mysql.rb +63 -0
  33. data/lib/hanami/model/sql/consoles/postgresql.rb +68 -0
  34. data/lib/hanami/model/sql/consoles/sqlite.rb +46 -0
  35. data/lib/hanami/model/sql/entity/schema.rb +125 -0
  36. data/lib/hanami/model/sql/types.rb +95 -0
  37. data/lib/hanami/model/sql/types/schema/coercions.rb +198 -0
  38. data/lib/hanami/model/types.rb +99 -0
  39. data/lib/hanami/model/version.rb +1 -1
  40. data/lib/hanami/repository.rb +287 -723
  41. metadata +77 -40
  42. data/EXAMPLE.md +0 -213
  43. data/lib/hanami/entity/dirty_tracking.rb +0 -74
  44. data/lib/hanami/model/adapters/abstract.rb +0 -281
  45. data/lib/hanami/model/adapters/file_system_adapter.rb +0 -288
  46. data/lib/hanami/model/adapters/implementation.rb +0 -111
  47. data/lib/hanami/model/adapters/memory/collection.rb +0 -132
  48. data/lib/hanami/model/adapters/memory/command.rb +0 -113
  49. data/lib/hanami/model/adapters/memory/query.rb +0 -653
  50. data/lib/hanami/model/adapters/memory_adapter.rb +0 -179
  51. data/lib/hanami/model/adapters/null_adapter.rb +0 -24
  52. data/lib/hanami/model/adapters/sql/collection.rb +0 -287
  53. data/lib/hanami/model/adapters/sql/command.rb +0 -88
  54. data/lib/hanami/model/adapters/sql/console.rb +0 -33
  55. data/lib/hanami/model/adapters/sql/consoles/mysql.rb +0 -49
  56. data/lib/hanami/model/adapters/sql/consoles/postgresql.rb +0 -48
  57. data/lib/hanami/model/adapters/sql/consoles/sqlite.rb +0 -26
  58. data/lib/hanami/model/adapters/sql/query.rb +0 -788
  59. data/lib/hanami/model/adapters/sql_adapter.rb +0 -296
  60. data/lib/hanami/model/coercer.rb +0 -74
  61. data/lib/hanami/model/config/adapter.rb +0 -116
  62. data/lib/hanami/model/config/mapper.rb +0 -45
  63. data/lib/hanami/model/mapper.rb +0 -124
  64. data/lib/hanami/model/mapping/attribute.rb +0 -85
  65. data/lib/hanami/model/mapping/coercers.rb +0 -314
  66. data/lib/hanami/model/mapping/collection.rb +0 -490
  67. data/lib/hanami/model/mapping/collection_coercer.rb +0 -79
data/hanami-model.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Hanami::Model::VERSION
9
9
  spec.authors = ['Luca Guidi', 'Trung Lê', 'Alfonso Uceda']
10
10
  spec.email = ['me@lucaguidi.com', 'trung.le@ruby-journal.com', 'uceda73@gmail.com']
11
- spec.summary = %q{A persistence layer for Hanami}
12
- spec.description = %q{A persistence framework with entities, repositories, data mapper and query objects}
11
+ spec.summary = 'A persistence layer for Hanami'
12
+ spec.description = 'A persistence framework with entities and repositories'
13
13
  spec.homepage = 'http://hanamirb.org'
14
14
  spec.license = 'MIT'
15
15
 
@@ -17,12 +17,15 @@ Gem::Specification.new do |spec|
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ['lib']
20
- spec.required_ruby_version = '>= 2.0.0'
20
+ spec.required_ruby_version = '>= 2.3.0'
21
21
 
22
- spec.add_runtime_dependency 'hanami-utils', '~> 0.7'
23
- spec.add_runtime_dependency 'sequel', '~> 4.9'
22
+ spec.add_runtime_dependency 'hanami-utils', '~> 0.8'
23
+ spec.add_runtime_dependency 'rom-sql', '~> 0.9'
24
+ spec.add_runtime_dependency 'rom-repository', '~> 0.3'
25
+ spec.add_runtime_dependency 'dry-types', '~> 0.9'
26
+ spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
24
27
 
25
28
  spec.add_development_dependency 'bundler', '~> 1.6'
26
29
  spec.add_development_dependency 'minitest', '~> 5'
27
- spec.add_development_dependency 'rake', '~> 10'
30
+ spec.add_development_dependency 'rake', '~> 11'
28
31
  end
data/lib/hanami/entity.rb CHANGED
@@ -1,5 +1,4 @@
1
- require 'hanami/utils/kernel'
2
- require 'hanami/utils/attributes'
1
+ require 'hanami/model/types'
3
2
 
4
3
  module Hanami
5
4
  # An object that is defined by its identity.
@@ -22,17 +21,8 @@ module Hanami
22
21
  #
23
22
  # class Person
24
23
  # include Hanami::Entity
25
- # attributes :name, :age
26
24
  # end
27
25
  #
28
- # When a class includes `Hanami::Entity` it receives the following interface:
29
- #
30
- # * #id
31
- # * #id=
32
- # * #initialize(attributes = {})
33
- #
34
- # `Hanami::Entity` also provides the `.attributes=` for defining attribute accessors for the given names.
35
- #
36
26
  # If we expand the code above in **pure Ruby**, it would be:
37
27
  #
38
28
  # @example Pure Ruby
@@ -60,239 +50,165 @@ module Hanami
60
50
  # @since 0.1.0
61
51
  #
62
52
  # @see Hanami::Repository
63
- module Entity
64
- # Inject the public API into the hosting class.
65
- #
66
- # @since 0.1.0
67
- #
68
- # @example With Object
69
- # require 'hanami/model'
70
- #
71
- # class User
72
- # include Hanami::Entity
73
- # end
74
- #
75
- # @example With Struct
76
- # require 'hanami/model'
53
+ class Entity
54
+ require 'hanami/entity/schema'
55
+
56
+ # Syntactic shortcut to reference types in custom schema DSL
77
57
  #
78
- # User = Struct.new(:id, :name) do
79
- # include Hanami::Entity
80
- # end
81
- def self.included(base)
82
- base.class_eval do
83
- extend ClassMethods
84
- attributes :id
85
- end
58
+ # @since 0.7.0
59
+ module Types
60
+ include Hanami::Model::Types
86
61
  end
87
62
 
63
+ # Class level interface
64
+ #
65
+ # @since 0.7.0
66
+ # @api private
88
67
  module ClassMethods
89
- # (Re)defines getters, setters and initialization for the given attributes.
90
- #
91
- # These attributes can match the database columns, but this isn't a
92
- # requirement. The mapper used by the relative repository will translate
93
- # these names automatically.
94
- #
95
- # An entity can work with attributes not configured in the mapper, but
96
- # of course they will be ignored when the entity will be persisted.
97
- #
98
- # Please notice that the required `id` attribute is automatically defined
99
- # and can be omitted in the arguments.
68
+ # Define manual entity schema
100
69
  #
101
- # @param attrs [Array<Symbol>] a set of arbitrary attribute names
70
+ # With a SQL database this setup happens automatically and you SHOULD NOT
71
+ # use this DSL. You should use only when you want to customize the automatic
72
+ # setup.
102
73
  #
103
- # @since 0.2.0
74
+ # If you're working with an entity that isn't "backed" by a SQL table or
75
+ # with a schema-less database, you may want to manually setup a set of
76
+ # attributes via this DSL. If you don't do any setup, the entity accepts all
77
+ # the given attributes.
104
78
  #
105
- # @see Hanami::Repository
106
- # @see Hanami::Model::Mapper
79
+ # @param blk [Proc] the block that defines the attributes
107
80
  #
108
- # @example
109
- # require 'hanami/model'
81
+ # @since 0.7.0
110
82
  #
111
- # class User
112
- # include Hanami::Entity
113
- # attributes :name, :age
114
- # end
115
- # User.attributes => #<Set: {:id, :name, :age}>
116
- #
117
- # @example Given params is array of attributes
118
- # require 'hanami/model'
119
- #
120
- # class User
121
- # include Hanami::Entity
122
- # attributes [:name, :age]
123
- # end
124
- # User.attributes => #<Set: {:id, :name, :age}>
125
- #
126
- # @example Extend entity
127
- # require 'hanami/model'
128
- #
129
- # class User
130
- # include Hanami::Entity
131
- # attributes :name
132
- # end
133
- #
134
- # class DeletedUser < User
135
- # include Hanami::Entity
136
- # attributes :deleted_at
137
- # end
138
- #
139
- # User.attributes => #<Set: {:id, :name}>
140
- # DeletedUser.attributes => #<Set: {:id, :name, :deleted_at}>
141
- #
142
- def attributes(*attrs)
143
- return @attributes ||= Set.new unless attrs.any?
144
-
145
- Hanami::Utils::Kernel.Array(attrs).each do |attr|
146
- if allowed_attribute_name?(attr)
147
- define_attr_accessor(attr)
148
- self.attributes << attr
149
- end
150
- end
83
+ # @see Hanami::Entity
84
+ def attributes(&blk)
85
+ self.schema = Schema.new(&blk)
86
+ @attributes = true
151
87
  end
152
88
 
153
- # Define setter/getter methods for attributes.
89
+ # Assign a schema
154
90
  #
155
- # @param attr [Symbol] an attribute name
91
+ # @param value [Hanami::Entity::Schema] the schema
156
92
  #
157
- # @since 0.3.1
93
+ # @since 0.7.0
158
94
  # @api private
159
- def define_attr_accessor(attr)
160
- attr_accessor(attr)
95
+ def schema=(value)
96
+ return if defined?(@attributes)
97
+ @schema = value
161
98
  end
162
99
 
163
- # Check if attr_reader define the given attribute
164
- #
165
- # @since 0.5.1
100
+ # @since 0.7.0
166
101
  # @api private
167
- def allowed_attribute_name?(name)
168
- !instance_methods.include?(name)
169
- end
170
-
171
- protected
102
+ attr_reader :schema
103
+ end
172
104
 
173
- # @see Class#inherited
174
- def inherited(subclass)
175
- subclass.attributes(*attributes)
176
- super
105
+ # @since 0.7.0
106
+ # @api private
107
+ def self.inherited(klass)
108
+ klass.class_eval do
109
+ @schema = Schema.new
110
+ extend ClassMethods
177
111
  end
178
112
  end
179
113
 
180
- # Defines a generic, inefficient initializer, in case that the attributes
181
- # weren't explicitly defined with `.attributes=`.
114
+ # Instantiate a new entity
182
115
  #
183
- # @param attributes [Hash] a set of attribute names and values
116
+ # @param attributes [Hash,#to_h,NilClass] data to initialize the entity
184
117
  #
185
- # @raise NoMethodError in case the given attributes are trying to set unknown
186
- # or private methods.
118
+ # @return [Hanami::Entity] the new entity instance
187
119
  #
188
- # @since 0.1.0
120
+ # @raise [TypeError] if the given attributes are invalid
189
121
  #
190
- # @see .attributes
191
- def initialize(attributes = {})
192
- attributes.each do |k, v|
193
- setter = "#{ k }="
194
- public_send(setter, v) if respond_to?(setter)
195
- end
122
+ # @since 0.1.0
123
+ def initialize(attributes = nil)
124
+ @attributes = self.class.schema[attributes]
125
+ freeze
196
126
  end
197
127
 
198
- # Overrides the equality Ruby operator
128
+ # Entity ID
199
129
  #
200
- # Two entities are considered equal if they are instances of the same class
201
- # and if they have the same #id.
130
+ # @return [Object,NilClass] the ID, if present
202
131
  #
203
- # @since 0.1.0
204
- def ==(other)
205
- self.class == other.class &&
206
- self.id == other.id
132
+ # @since 0.7.0
133
+ def id
134
+ attributes.fetch(:id, nil)
207
135
  end
208
136
 
209
- # Return the hash of attributes
210
- #
211
- # @since 0.2.0
137
+ # Handle dynamic accessors
212
138
  #
213
- # @example
214
- # require 'hanami/model'
215
- # class User
216
- # include Hanami::Entity
217
- # attributes :name
218
- # end
139
+ # If internal attributes set has the requested key, it returns the linked
140
+ # value, otherwise it raises a <tt>NoMethodError</tt>
219
141
  #
220
- # user = User.new(id: 23, name: 'Luca')
221
- # user.to_h # => { :id => 23, :name => "Luca" }
222
- def to_h
223
- Hash[attribute_names.map { |a| [a, read_attribute(a)] }]
142
+ # @since 0.7.0
143
+ def method_missing(m, *)
144
+ attribute?(m) or super # rubocop:disable Style/AndOr
145
+ attributes.fetch(m, nil)
224
146
  end
225
147
 
226
- # Return the set of attribute names
148
+ # Implement generic equality for entities
227
149
  #
228
- # @since 0.5.1
150
+ # Two entities are equal if they are instances of the same class and they
151
+ # have the same id.
229
152
  #
230
- # @example
231
- # require 'hanami/model'
232
- # class User
233
- # include Hanami::Entity
234
- # attributes :name
235
- # end
153
+ # @param other [Object] the object of comparison
236
154
  #
237
- # user = User.new(id: 23, name: 'Luca')
238
- # user.attribute_names # #<Set: {:id, :name}>
239
- def attribute_names
240
- self.class.attributes
155
+ # @return [FalseClass,TrueClass] the result of the check
156
+ #
157
+ # @since 0.1.0
158
+ def ==(other)
159
+ self.class == other.class &&
160
+ id == other.id
241
161
  end
242
162
 
243
- # Return the contents of the entity as a nicely formatted string.
163
+ # Implement predictable hashing for hash equality
244
164
  #
245
- # Display all attributes of the entity for inspection (even if they are nil)
165
+ # @return [Integer] the object hash
246
166
  #
247
- # @since 0.5.1
248
- #
249
- # @example
250
- # require 'hanami/model'
251
- # class User
252
- # include Hanami::Entity
253
- # attributes :name, :email
254
- # end
255
- #
256
- # user = User.new(id: 23, name: 'Luca')
257
- # user.inspect # #<User:0x007fa7eefe0b58 @id=nil @name="Luca" @email=nil>
258
- def inspect
259
- attr_list = attribute_names.inject([]) do |res, name|
260
- res << "@#{name}=#{read_attribute(name).inspect}"
261
- end.join(' ')
262
-
263
- "#<#{self.class.name}:0x00#{(__id__ << 1).to_s(16)} #{attr_list}>"
167
+ # @since 0.7.0
168
+ def hash
169
+ [self.class, id].hash
264
170
  end
265
171
 
266
- alias_method :to_s, :inspect
172
+ # Freeze the entity
173
+ #
174
+ # @since 0.7.0
175
+ def freeze
176
+ attributes.freeze
177
+ super
178
+ end
267
179
 
268
- # Set attributes for entity
180
+ # Serialize entity to a Hash
269
181
  #
270
- # @since 0.2.0
182
+ # @return [Hash] the result of serialization
271
183
  #
272
- # @example
273
- # require 'hanami/model'
274
- # class User
275
- # include Hanami::Entity
276
- # attributes :name
277
- # end
184
+ # @since 0.1.0
185
+ def to_h
186
+ attributes.deep_dup.to_h
187
+ end
188
+
189
+ # @since 0.7.0
190
+ alias to_hash to_h
191
+
192
+ protected
193
+
194
+ # Check if the attribute is allowed to be read
278
195
  #
279
- # user = User.new(name: 'Lucca')
280
- # user.update(name: 'Luca')
281
- # user.name # => 'Luca'
282
- def update(attributes={})
283
- attributes.each do |attribute, value|
284
- public_send("#{attribute}=", value)
285
- end
196
+ # @since 0.7.0
197
+ # @api private
198
+ def attribute?(name)
199
+ self.class.schema.attribute?(name)
286
200
  end
287
201
 
288
202
  private
289
203
 
290
- # Return the value by attribute name
291
- #
292
- # @since 0.5.1
204
+ # @since 0.1.0
205
+ # @api private
206
+ attr_reader :attributes
207
+
208
+ # @since 0.7.0
293
209
  # @api private
294
- def read_attribute(attr_name)
295
- public_send(attr_name)
210
+ def respond_to_missing?(name, _include_all)
211
+ attribute?(name)
296
212
  end
297
213
  end
298
214
  end
@@ -0,0 +1,236 @@
1
+ require 'hanami/model/types'
2
+ require 'hanami/utils/hash'
3
+
4
+ module Hanami
5
+ class Entity
6
+ # Entity schema is a definition of a set of typed attributes.
7
+ #
8
+ # @since 0.7.0
9
+ # @api private
10
+ #
11
+ # @example SQL Automatic Setup
12
+ # require 'hanami/model'
13
+ #
14
+ # class Account
15
+ # include Hanami::Entity
16
+ # end
17
+ #
18
+ # account = Account.new(name: "Acme Inc.")
19
+ # account.name # => "Hanami"
20
+ #
21
+ # account = Account.new(foo: "bar")
22
+ # account.foo # => NoMethodError
23
+ #
24
+ # @example Non-SQL Manual Setup
25
+ # require 'hanami/model'
26
+ #
27
+ # class Account
28
+ # include Hanami::Entity
29
+ #
30
+ # attributes do
31
+ # attribute :id, Types::Int
32
+ # attribute :name, Types::String
33
+ # attribute :codes, Types::Array(Types::Int)
34
+ # attribute :users, Types::Array(User)
35
+ # attribute :email, Types::String.constrained(format: /@/)
36
+ # attribute :created_at, Types::DateTime
37
+ # end
38
+ # end
39
+ #
40
+ # account = Account.new(name: "Acme Inc.")
41
+ # account.name # => "Acme Inc."
42
+ #
43
+ # account = Account.new(foo: "bar")
44
+ # account.foo # => NoMethodError
45
+ #
46
+ # @example Schemaless Entity
47
+ # require 'hanami/model'
48
+ #
49
+ # class Account
50
+ # include Hanami::Entity
51
+ # end
52
+ #
53
+ # account = Account.new(name: "Acme Inc.")
54
+ # account.name # => "Acme Inc."
55
+ #
56
+ # account = Account.new(foo: "bar")
57
+ # account.foo # => "bar"
58
+ class Schema
59
+ # Schemaless entities logic
60
+ #
61
+ # @since 0.7.0
62
+ # @api private
63
+ class Schemaless
64
+ # @since 0.7.0
65
+ # @api private
66
+ def initialize
67
+ freeze
68
+ end
69
+
70
+ # @param attributes [#to_hash] the attributes hash
71
+ #
72
+ # @return [Hash]
73
+ #
74
+ # @since 0.7.0
75
+ # @api private
76
+ def call(attributes)
77
+ if attributes.nil?
78
+ {}
79
+ else
80
+ attributes.dup
81
+ end
82
+ end
83
+
84
+ # @since 0.7.0
85
+ # @api private
86
+ def attribute?(_name)
87
+ true
88
+ end
89
+ end
90
+
91
+ # Schema definition
92
+ #
93
+ # @since 0.7.0
94
+ # @api private
95
+ class Definition
96
+ # Schema DSL
97
+ #
98
+ # @since 0.7.0
99
+ class Dsl
100
+ # @since 0.7.0
101
+ # @api private
102
+ def self.build(&blk)
103
+ attributes = new(&blk).to_h
104
+ [attributes, Hanami::Model::Types::Coercible::Hash.schema(attributes)]
105
+ end
106
+
107
+ # @since 0.7.0
108
+ # @api private
109
+ def initialize(&blk)
110
+ @attributes = {}
111
+ instance_eval(&blk)
112
+ end
113
+
114
+ # Define an attribute
115
+ #
116
+ # @param name [Symbol] the attribute name
117
+ # @param type [Dry::Types::Definition] the attribute type
118
+ #
119
+ # @since 0.7.0
120
+ def attribute(name, type)
121
+ @attributes[name] = type
122
+ end
123
+
124
+ # @since 0.7.0
125
+ # @api private
126
+ def to_h
127
+ @attributes
128
+ end
129
+ end
130
+
131
+ # Instantiate a new DSL instance for an entity
132
+ #
133
+ # @param blk [Proc] the block that defines the attributes
134
+ #
135
+ # @return [Hanami::Entity::Schema::Dsl] the DSL
136
+ #
137
+ # @since 0.7.0
138
+ # @api private
139
+ def initialize(&blk)
140
+ raise LocalJumpError unless block_given?
141
+ @attributes, @schema = Dsl.build(&blk)
142
+ @attributes = Hash[@attributes.map { |k, _| [k, true] }]
143
+ freeze
144
+ end
145
+
146
+ # Process attributes
147
+ #
148
+ # @param attributes [#to_hash] the attributes hash
149
+ #
150
+ # @raise [TypeError] if the process fails
151
+ #
152
+ # @since 0.7.0
153
+ # @api private
154
+ def call(attributes)
155
+ schema.call(attributes)
156
+ rescue Dry::Types::SchemaError => e
157
+ raise TypeError.new(e.message)
158
+ end
159
+
160
+ # Check if the attribute is known
161
+ #
162
+ # @param name [Symbol] the attribute name
163
+ #
164
+ # @return [TrueClass,FalseClass] the result of the check
165
+ #
166
+ # @since 0.7.0
167
+ # @api private
168
+ def attribute?(name)
169
+ attributes.key?(name)
170
+ end
171
+
172
+ private
173
+
174
+ # @since 0.7.0
175
+ # @api private
176
+ attr_reader :schema
177
+
178
+ # @since 0.7.0
179
+ # @api private
180
+ attr_reader :attributes
181
+ end
182
+
183
+ # Build a new instance of Schema with the attributes defined by the given block
184
+ #
185
+ # @param blk [Proc] the optional block that defines the attributes
186
+ #
187
+ # @return [Hanami::Entity::Schema] the schema
188
+ #
189
+ # @since 0.7.0
190
+ # @api private
191
+ def initialize(&blk)
192
+ @schema = if block_given?
193
+ Definition.new(&blk)
194
+ else
195
+ Schemaless.new
196
+ end
197
+ end
198
+
199
+ # Process attributes
200
+ #
201
+ # @param attributes [#to_hash] the attributes hash
202
+ #
203
+ # @raise [TypeError] if the process fails
204
+ #
205
+ # @since 0.7.0
206
+ # @api private
207
+ def call(attributes)
208
+ Utils::Hash.new(
209
+ schema.call(attributes)
210
+ ).symbolize!
211
+ end
212
+
213
+ # @since 0.7.0
214
+ # @api private
215
+ alias [] call
216
+
217
+ # Check if the attribute is known
218
+ #
219
+ # @param name [Symbol] the attribute name
220
+ #
221
+ # @return [TrueClass,FalseClass] the result of the check
222
+ #
223
+ # @since 0.7.0
224
+ # @api private
225
+ def attribute?(name)
226
+ schema.attribute?(name)
227
+ end
228
+
229
+ protected
230
+
231
+ # @since 0.7.0
232
+ # @api private
233
+ attr_reader :schema
234
+ end
235
+ end
236
+ end