1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
|
# frozen_string_literal: true
require "dry/core/constants"
require "dry/core/errors"
module Dry
module Core
# Internal support module for class-level settings
#
# @api public
module ClassAttributes
include Constants
# Specify what attributes a class will use
#
# @example
# class ExtraClass
# extend Dry::Core::ClassAttributes
#
# defines :hello
#
# hello 'world'
# end
#
# @example with inheritance and type checking
#
# class MyClass
# extend Dry::Core::ClassAttributes
#
# defines :one, :two, type: Integer
#
# one 1
# two 2
# end
#
# class OtherClass < MyClass
# two 3
# end
#
# MyClass.one # => 1
# MyClass.two # => 2
#
# OtherClass.one # => 1
# OtherClass.two # => 3
#
# @example with dry-types
#
# class Foo
# extend Dry::Core::ClassAttributes
#
# defines :one, :two, type: Dry::Types['strict.int']
# end
#
# @example with coercion using Proc
#
# class Bar
# extend Dry::Core::ClassAttributes
#
# defines :one, coerce: proc { |value| value.to_s }
# end
#
# @example with coercion using dry-types
#
# class Bar
# extend Dry::Core::ClassAttributes
#
# defines :one, coerce: Dry::Types['coercible.string']
# end
#
def defines(*args, type: ::Object, coerce: IDENTITY) # rubocop:disable Metrics/PerceivedComplexity
unless coerce.respond_to?(:call)
raise ::ArgumentError, "Non-callable coerce option: #{coerce.inspect}"
end
mod = ::Module.new do
args.each do |name|
ivar = :"@#{name}"
define_method(name) do |value = Undefined|
if Undefined.equal?(value)
if instance_variable_defined?(ivar)
instance_variable_get(ivar)
else # rubocop:disable Style/EmptyElse
nil
end
elsif type === value # rubocop:disable Style/CaseEquality
instance_variable_set(ivar, coerce.call(value))
else
raise InvalidClassAttributeValue.new(name, value)
end
end
end
define_method(:inherited) do |klass|
args.each { |name| klass.send(name, send(name)) }
super(klass)
end
end
extend(mod)
end
end
end
end
|