Module: Dry::Struct::ClassInterface

Includes:
Core::ClassAttributes, Types::Builder, Types::Type
Included in:
Dry::Struct
Defined in:
lib/dry/struct/class_interface.rb

Overview

Class-level interface of Dry::Struct and Value

Instance Method Summary collapse

Instance Method Details

#attribute(name, type = nil) { ... } ⇒ Dry::Struct

Adds an attribute for this Dry::Struct with given name and type and modifies #schema accordingly.

Examples:

with nested structs

class Language < Dry::Struct
  attribute :name, Types::String
  attribute :details, Dry::Struct do
    attribute :type, Types::String
  end
end

Language.schema
  #=> {
        :name=>#<Dry::Types::Definition primitive=String options={} meta={}>,
        :details=>Language::Details
      }

ruby = Language.new(name: 'Ruby', details: { type: 'OO' })
ruby.name #=> 'Ruby'
ruby.details #=> #<Language::Details type="OO">
ruby.details.type #=> 'OO'

with a nested array of structs

class Language < Dry::Struct
  attribute :name, Types::String
  array :versions, Types::String
  array :celebrities, Types::Array.of(Dry::Struct) do
    attribute :name, Types::String
    attribute :pseudonym, Types::String
  end
end

Language.schema
  #=> {
        :name=>#<Dry::Types::Definition primitive=String options={} meta={}>,
        :versions=>#<Dry::Types::Array::Member primitive=Array options={:member=>#<Dry::Types::Definition primitive=String options={} meta={}>} meta={}>,
        :celebrities=>#<Dry::Types::Array::Member primitive=Array options={:member=>Language::Celebrity} meta={}>
      }

ruby = Language.new(
  name: 'Ruby',
  versions: %w(1.8.7 1.9.8 2.0.1),
  celebrities: [
    { name: 'Yukihiro Matsumoto', pseudonym: 'Matz' },
    { name: 'Aaron Patterson', pseudonym: 'tenderlove' }
  ]
)
ruby.name #=> 'Ruby'
ruby.versions #=> ['1.8.7', '1.9.8', '2.0.1']
ruby.celebrities
  #=> [
        #<Language::Celebrity name='Yukihiro Matsumoto' pseudonym='Matz'>,
        #<Language::Celebrity name='Aaron Patterson' pseudonym='tenderlove'>
      ]
ruby.celebrities[0].name #=> 'Yukihiro Matsumoto'
ruby.celebrities[0].pseudonym #=> 'Matz'
ruby.celebrities[1].name #=> 'Aaron Patterson'
ruby.celebrities[1].pseudonym #=> 'tenderlove'

Yields:

  • If a block is given, it will be evaluated in the context of a new struct class, and set as a nested type for the given attribute. A class with a matching name will also be defined for the nested type.

Raises:



102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/dry/struct/class_interface.rb', line 102

def attribute(name, type = nil, &block)
  if block
    type = Dry::Types[type] if type.is_a?(String)
    type = struct_builder.(name, type, &block)
  elsif type.nil?
    raise(
      ArgumentError,
      'you must supply a type or a block to `Dry::Struct.attribute`'
    )
  end

  attributes(name => type)
end

#attribute?(key) ⇒ Boolean

Checks if this Dry::Struct has the given attribute



300
301
302
# File 'lib/dry/struct/class_interface.rb', line 300

def attribute?(key)
  schema.key?(key)
end

#attribute_namesArray<Symbol>

Gets the list of attribute names



312
313
314
# File 'lib/dry/struct/class_interface.rb', line 312

def attribute_names
  @attribute_names ||= schema.keys
end

#attributes(new_schema) ⇒ Dry::Struct

Examples:

class Book < Dry::Struct
  attributes(
    title: Types::String,
    author: Types::String
  )
end

Book.schema
  #=> {title: #<Dry::Types::Definition primitive=String options={}>,
  #    author: #<Dry::Types::Definition primitive=String options={}>}

Raises:

See Also:



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/dry/struct/class_interface.rb', line 132

def attributes(new_schema)
  check_schema_duplication(new_schema)

  input input.schema(new_schema)

  new_schema.each_key do |key|
    next if instance_methods.include?(key)
    class_eval(<<-RUBY)
      def #{ key }
        @attributes[#{ key.inspect }]
      end
    RUBY
  end

  @attribute_names = nil

  descendants.
    select { |d| d.superclass == self }.
    each { |d| d.attributes(new_schema.reject { |k, _| d.schema.key?(k) }) }

  self
end

#call(attributes = default_attributes) ⇒ Dry::Struct Also known as: []

Calls type constructor. The behavior is identical to .new but returns the input back if it's a subclass of the struct.



216
217
218
219
# File 'lib/dry/struct/class_interface.rb', line 216

def call(attributes = default_attributes)
  return attributes if attributes.is_a?(self)
  new(attributes)
end

#constrained?true



282
283
284
# File 'lib/dry/struct/class_interface.rb', line 282

def constrained?
  true
end

#constructor(constructor = nil, **_options, &block) ⇒ Dry::Struct::Constructor



226
227
228
# File 'lib/dry/struct/class_interface.rb', line 226

def constructor(constructor = nil, **_options, &block)
  Struct::Constructor.new(self, fn: constructor || block)
end

#default?false



271
272
273
# File 'lib/dry/struct/class_interface.rb', line 271

def default?
  false
end

#failure(*args) ⇒ Dry::Types::Result::Failure



260
261
262
# File 'lib/dry/struct/class_interface.rb', line 260

def failure(*args)
  result(::Dry::Types::Result::Failure, *args)
end

#inherited(klass) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/dry/struct/class_interface.rb', line 19

def inherited(klass)
  super

  base = self

  klass.class_eval do
    @meta = base.meta

    unless equal?(Value)
      extend Dry::Core::DescendantsTracker
    end
  end
end

#meta(meta = Undefined) ⇒ {Symbol => Object}



317
318
319
320
321
322
323
324
325
# File 'lib/dry/struct/class_interface.rb', line 317

def meta(meta = Undefined)
  if meta.equal?(Undefined)
    @meta
  else
    Class.new(self) do
      @meta = @meta.merge(meta) unless meta.empty?
    end
  end
end

#new(attributes = default_attributes) ⇒ Object

Raises:



201
202
203
204
205
206
207
208
209
# File 'lib/dry/struct/class_interface.rb', line 201

def new(attributes = default_attributes)
  if attributes.instance_of?(self)
    attributes
  else
    super(input[attributes])
  end
rescue Types::SchemaError, Types::MissingKeyError, Types::UnknownKeysError => error
  raise Struct::Error, "[#{self}.new] #{error}"
end

#optional?false



292
293
294
# File 'lib/dry/struct/class_interface.rb', line 292

def optional?
  false
end

#primitiveself



287
288
289
# File 'lib/dry/struct/class_interface.rb', line 287

def primitive
  self
end

#result(klass, *args) ⇒ Object



266
267
268
# File 'lib/dry/struct/class_interface.rb', line 266

def result(klass, *args)
  klass.new(*args)
end

#schemaHash{Symbol => Dry::Types::Definition, Dry::Struct}



305
306
307
# File 'lib/dry/struct/class_interface.rb', line 305

def schema
  input.member_types
end

#success(*args) ⇒ Dry::Types::Result::Success



254
255
256
# File 'lib/dry/struct/class_interface.rb', line 254

def success(*args)
  result(Types::Result::Success, *args)
end

#transform_keys(proc = nil, &block) ⇒ Object

Add an arbitrary transformation for input hash keys.

Examples:

class Book < Dry::Struct
  transform_keys(&:to_sym)

  attribute :title, Types::Strict::String
end

Book.new('title' => "The Old Man and the Sea")
# => #<Book title="The Old Man and the Sea">


185
186
187
# File 'lib/dry/struct/class_interface.rb', line 185

def transform_keys(proc = nil, &block)
  input input.with_key_transform(proc || block)
end

#transform_types(proc = nil, &block) ⇒ Object

Add an arbitrary transformation for new attribute types.

Examples:

class Book < Dry::Struct
  transform_types { |t| t.meta(struct: :Book) }

  attribute :title, Types::Strict::String
end

Book.schema[:title].meta # => { struct: :Book }


168
169
170
# File 'lib/dry/struct/class_interface.rb', line 168

def transform_types(proc = nil, &block)
  input input.with_type_transform(proc || block)
end

#try(input) {|failure| ... } ⇒ Dry::Types::Result

Yield Parameters:

  • failure (Dry::Types::Result::Failure)

Yield Returns:

  • (Dry::Types::ResultResult)


234
235
236
237
238
239
# File 'lib/dry/struct/class_interface.rb', line 234

def try(input)
  Types::Result::Success.new(self[input])
rescue Struct::Error => e
  failure = Types::Result::Failure.new(input, e.message)
  block_given? ? yield(failure) : failure
end

#try_struct(input) ⇒ Dry::Types::Result



244
245
246
247
248
249
250
# File 'lib/dry/struct/class_interface.rb', line 244

def try_struct(input)
  if input.is_a?(self)
    Types::Result::Success.new(input)
  else
    yield
  end
end

#valid?(value) ⇒ Boolean



277
278
279
# File 'lib/dry/struct/class_interface.rb', line 277

def valid?(value)
  self === value
end

#|(type) ⇒ Dry::Types::Sum

Build a sum type



330
331
332
333
334
335
336
# File 'lib/dry/struct/class_interface.rb', line 330

def |(type)
  if type.is_a?(Class) && type <= Struct
    Struct::Sum.new(self, type)
  else
    super
  end
end