File: class_attributes.rb

package info (click to toggle)
ruby-dry-core 0.7.1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 396 kB
  • sloc: ruby: 2,126; sh: 4; makefile: 4
file content (105 lines) | stat: -rw-r--r-- 2,642 bytes parent folder | download
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