Class: Mimi::Struct

Inherits:
Core::Struct
  • Object
show all
Defined in:
lib/mimi/struct.rb

Overview

A Struct that can be initialized from a Hash or a PORO.

A Struct declares its attributes and rules, which define how its attributes are mapped from input data.

Defined Under Namespace

Classes: Error

Constant Summary collapse

VERSION =
Mimi::Struct_VERSION
DEFAULT_ATTRIBUTE_MAPPER =

Default attribute mapper

Maps value of the source attribute to the target attribute. Calculates a default value if the source attribute is not set.

-> (o, params) do
  source_attr_set = o.respond_to?(params[:from]) && !o.send(params[:from]).nil?
  if source_attr_set || !params.key?(:default)
    o.send(params[:from])
  else
    # source attr is not set AND there is :default
    if params[:default].is_a?(Proc)
      call_as_proc(params[:default], o, params) # default as proc
    else
      params[:default] # default as literal value
    end
  end
end
DEFAULT_IF_FOR_OPTIONAL =

Default :if block for an optional attribute

Skips the attribute if the source attribute is not set.

-> (o, params) do
  o.respond_to?(params[:from])
end

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source = {}) ⇒ Struct

Creates a mapped Struct object from another object

Parameters:

  • source (Hash, Object) (defaults to: {})


52
53
54
55
56
57
58
# File 'lib/mimi/struct.rb', line 52

def initialize(source = {})
  source = Mimi::Core::Struct.new(source) if source.is_a?(Hash)
  attributes = self.class.transform_attributes(source)
  super(attributes)
rescue StandardError => e
  raise e.class, "Failed to construct #{self.class}: #{e}", e.backtrace
end

Class Method Details

.<<(obj_or_collection) ⇒ Object

Converts a single object or collection to Struct.



135
136
137
138
139
140
141
# File 'lib/mimi/struct.rb', line 135

def self.<<(obj_or_collection)
  if obj_or_collection.is_a?(Array)
    obj_or_collection.map { |o| self << o }
  else
    new(obj_or_collection)
  end
end

.attribute(name, params = {}) ⇒ Object

An attribute definition

Possible params:

from: <Symbol>
using: <proc,Mimi::Struct>
if: <proc>
default: <proc,Object>
optional: <true,false>

Parameters:

  • name (Symbol)
  • params (Hash) (defaults to: {})

Raises:

  • (ArgumentError)


90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/mimi/struct.rb', line 90

def self.attribute(name, params = {})
  name = name.to_sym
  raise ArgumentError, "Attribute '#{name}' is already declared" if attribute_definitions.key?(name)
  defaults = group_params.reduce(:merge).merge(
    from: name,
    using: DEFAULT_ATTRIBUTE_MAPPER
  )
  params = defaults.merge(params)
  if params.key?(:if) && params.key?(:optional)
    raise ArgumentError, "Keys :if and :optional cannot be used together"
  end
  params[:if] = DEFAULT_IF_FOR_OPTIONAL if params[:optional]
  add_attribute_definition(name, params)
end

.group(params, &block) ⇒ Object

Declare a group of parameters with common options

E.g.

Class User < Mimi::Struct
  attribute :id
  attribute :type
  attribute :name
  group if: -> (o) { o.type == 'ADMIN' } do
    attribute :admin_role
    attribute :admin_domain
  end
  group default: -> { Time.now.utc } do
    attribute :created_at
    attribute :updated_at
  end
end

NOTE: Not reentrable.

Parameters:

  • params (Hash)


126
127
128
129
130
# File 'lib/mimi/struct.rb', line 126

def self.group(params, &block)
  group_params << params
  yield
  group_params.pop
end

.transform_attributes(source) ⇒ Hash

Transform attributes according to rules

Parameters:

Returns:

  • (Hash)

    map of attribute name -> value



185
186
187
188
189
190
191
192
193
# File 'lib/mimi/struct.rb', line 185

def self.transform_attributes(source)
  result = attribute_definitions.map do |k, params|
    if params[:if].is_a?(Proc)
      next unless call_as_proc(params[:if], source, params)
    end
    [k, transform_single_attribute(source, k, params)]
  end.compact.to_h
  result
end

.value_to_h(value) ⇒ Object

Map value or values to Hash

Parameters:

  • value (Object)


230
231
232
233
234
235
236
237
238
239
# File 'lib/mimi/struct.rb', line 230

def self.value_to_h(value)
  case value
  when Struct
    value.to_h
  when Array
    value.map { |v| value_to_h(v) }
  else
    value
  end
end

Instance Method Details

#attributesHash

Returns attributes of this Structs as a Hash

Returns:

  • (Hash)


74
75
76
# File 'lib/mimi/struct.rb', line 74

def attributes
  @attributes
end

#to_hHash

Presents this Struct as a Hash, deeply converting nested Structs

Returns:

  • (Hash)


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

def to_h
  attributes.map do |k, v|
    [k, self.class.value_to_h(v)]
  end.to_h
end