Class: Typed::Struct

Inherits:
Object
  • Object
show all
Extended by:
Builder::BaseType
Defined in:
lib/typed/struct.rb

Defined Under Namespace

Classes: Updater

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Builder::BaseType

call, constrained, constructor, default, enum, instance, missable, nullable, process, |

Constructor Details

#initialize(input_data = {}) ⇒ Struct

Returns a new instance of Struct.



142
143
144
145
146
147
# File 'lib/typed/struct.rb', line 142

def initialize(input_data = {})
    case input_data
    when Typed::Builder::Result then initialize_from_result(input_data)
    else initialize_from_result(self.class.parse_as_hash(input_data))
    end
end

Class Method Details

.allow_extra_keys(new_flag) ⇒ Object



53
54
55
# File 'lib/typed/struct.rb', line 53

def allow_extra_keys(new_flag)
    define_singleton_method(:allow_extra_keys?) { new_flag }
end

.allow_extra_keys?Boolean

Returns:



57
58
59
# File 'lib/typed/struct.rb', line 57

def allow_extra_keys?
    true
end

.attribute(name, type = Typed.any) ⇒ Object

Raises:



36
37
38
39
40
41
42
43
44
45
# File 'lib/typed/struct.rb', line 36

def attribute(name, type = Typed.any)
    expected_type(type)

    name = name.to_sym

    raise Typed::InvalidType, "Property already defined: #{name}" if typed_attributes.key?(name)

    typed_attributes[name] = type
    define_method(name) { @_data.fetch(name) { Typed::Undefined } }
end

.parse_as_hash(input_data) ⇒ Object



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
# File 'lib/typed/struct.rb', line 70

def parse_as_hash(input_data)
    return Typed::Builder::Result.success(input_data.to_h) if input_data.is_a?(self)

    # TODO: remove this hack
    unless input_data.is_a?(::Hash) || input_data.class.name == 'ActionController::Parameters'
        return Typed::Builder::Result.failure { "Expected Hash, got #{input_data.inspect}" }
    end

    # Start by creating a new "clean" hash from input
    # This way, we can easily handle some variants (ActionController::Parameters, ...)
    clean_data = Hash.new { ::Typed::Undefined }
    input_data.each { |key, value| clean_data[key.to_sym] = value }

    # Check presence of extra keys
    extra_property = (clean_data.keys - schema.keys).first
    if extra_property && !allow_extra_keys?
        return Typed::Builder::Result
            .failure("Unknown property '#{extra_property}' of #{inspect}")
    end

    # Construct the final hash which will be stored internally to represent
    # Struct's data.
    output = schema.each_with_object({}) { |(name, type), acc|
        result = type.process(clean_data[name])

        unless result.ok
            return Typed::Builder::Result.failure {
                "Invalid property '#{name}' of #{inspect}: #{result.message}"
            }
        end

        acc[name] = result.value unless Typed::Undefined.equal?(result.value)
    }.freeze

    Typed::Builder::Result.success(output)
end

.process(data) ⇒ Object



65
66
67
68
# File 'lib/typed/struct.rb', line 65

def process(data)
    result = parse_as_hash(data)
    result.ok ? Typed::Builder::Result.success(new(result)) : result
end

.schemaObject



47
48
49
50
51
# File 'lib/typed/struct.rb', line 47

def schema
    @schema ||= ancestors.select { |a| Typed::Struct > a }.reverse.reduce({}) { |acc, clazz|
        acc.merge(clazz.typed_attributes)
    }.freeze
end

.typed_attributesObject



61
62
63
# File 'lib/typed/struct.rb', line 61

def typed_attributes
    @typed_attributes ||= {}
end

Instance Method Details

#==(other) ⇒ Object



127
128
129
130
131
132
# File 'lib/typed/struct.rb', line 127

def ==(other)
    return true if other.equal?(self)
    return false unless other.instance_of?(self.class)

    @_data == other.instance_variable_get(:@_data)
end

#[](key) ⇒ Object

Raises:



121
122
123
124
125
# File 'lib/typed/struct.rb', line 121

def [](key)
    raise Typed::InvalidType, "Unknown property: #{key.inspect}" unless self.class.schema.key?(key)

    @_data.fetch(key) { Typed::Undefined }
end

#hashObject



134
135
136
# File 'lib/typed/struct.rb', line 134

def hash
    @_data.hash
end

#inspectObject



112
113
114
115
# File 'lib/typed/struct.rb', line 112

def inspect
    attrs = self.class.schema.keys.map { |key| " #{key}=#{@_data[key].inspect}" }.join
    "#<#{self.class.name || self.class.inspect}#{attrs}>"
end

#key?(key) ⇒ Boolean

Returns:



138
139
140
# File 'lib/typed/struct.rb', line 138

def key?(key)
    @_data.key?(key)
end

#to_hObject



117
118
119
# File 'lib/typed/struct.rb', line 117

def to_h
    @_data
end

#updater(target) ⇒ Object



108
109
110
# File 'lib/typed/struct.rb', line 108

def updater(target)
    Updater.new(target, self)
end