class Mimi::Struct
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.
Constants
- 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.
- DEFAULT_IF_FOR_OPTIONAL
Default :if block for an optional attribute
Skips the attribute if the source attribute is not set.
- VERSION
Public Class Methods
Converts a single object or collection to Struct
.
# 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
An attribute definition
Possible params:
from: <Symbol> using: <proc,Mimi::Struct> if: <proc> default: <proc,Object> optional: <true,false>
@param name [Symbol] @param params [Hash]
# 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
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.
@param params [Hash]
# File lib/mimi/struct.rb, line 126 def self.group(params, &block) group_params << params yield group_params.pop end
Creates a mapped Struct
object from another object
@param source [Hash,Object]
# 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
Transform attributes according to rules
@param source [Struct] @return [Hash] map of attribute name -> value
# 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
Map value or values to Hash
@param value [Object]
# 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
Protected Class Methods
# File lib/mimi/struct.rb, line 152 def attribute_definitions @attribute_definitions ||= {} # merge with superclass attribute definitions superclass_attribute_definitions = superclass < Mimi::Struct ? superclass.attribute_definitions : {} superclass_attribute_definitions.merge(@attribute_definitions) # defined?(super) ? super.merge(@attribute_definitions) : @attribute_definitions end
Private Class Methods
Adds a new attribute definition
@param name [Symbol] @param params [Hash]
# File lib/mimi/struct.rb, line 167 def self.add_attribute_definition(name, params) @attribute_definitions ||= {} @attribute_definitions[name] = params.dup end
Calls a lambda as a proc, not caring about the number of arguments
@param proc_or_lambda [Proc] @param *args
# File lib/mimi/struct.rb, line 217 def self.call_as_proc(proc, *args) raise ArgumentError, "Proc is expected as proc" unless proc.is_a?(Proc) if proc.lambda? proc.call(*args.first(proc.arity)) else proc.call(*args) end end
Returns current stack of group parameters
@return [Array<Hash>]
# File lib/mimi/struct.rb, line 176 def self.group_params @group_params ||= [{}] end
Transforms a single attribute value according to rules passed as params
@param source [Struct] @param key [Symbol] @param params [Hash] transformation rules @return [Object]
# File lib/mimi/struct.rb, line 202 def self.transform_single_attribute(source, key, params) return call_as_proc(params[:using], source, params) if params[:using].is_a?(Proc) if params[:using].is_a?(Class) && params[:using] < Mimi::Struct return params[:using] << source.send(params[:from]) end raise "unexpected :using type: #{params[:using].class}" rescue StandardError => e raise Error, "Failed to transform attribute :#{key} : #{e}" end
Public Instance Methods
Returns attributes of this Structs as a Hash
@return [Hash]
# File lib/mimi/struct.rb, line 74 def attributes @attributes end
Presents this Struct
as a Hash, deeply converting nested Structs
@return [Hash]
# 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