module Eco module Language module Models # Basic class to define a parser/serializing framework # @attr_reader attr [String, Symbol] the attribute this parser/serializer is linked to. class ParserSerializer attr_reader :attr # Parser/serializer. # @param attr [String, Symbol] name of the parsed/serialized. # @param dependencies [Hash] provisioning of _**default dependencies**_ that will be required when calling back to the # parsing or serializing functions. def initialize(attr, dependencies: {}) @attr = attr @dependencies = dependencies @parser = {} @serializer = {} end # Defines the _parser_ of the attribute. # @note # 1. the _block_ should expect one or two parameters. # 2. the final dependencies is a merge of _default dependencies_ with `parse` call dependencies. # @param category [Symbol] a way to classify multiple parsers by category. # @yield [source_data, dependencies] user defined parser that returns the parsed value. # @yieldparam source_data [Any] source data that will be parsed. # @yieldparam dependencies [Hash] hash with the provisioned dependencies. def def_parser(category = :default, &block) @parser[category.to_sym] = block self end # Defines the _serializer_ of the attribute. # @note # 1. the block should expect one or two parameters. # 2. the final dependencies is a merge of _default dependencies_ with `serialize` call dependencies. # @param category [Symbol] a way to classify multiple serializers by category. # @yield [source_data, dependencies] user defined serialiser that returns the serialised value. # @yieldparam source_data [Any] source data that will be serialised. # @yieldparam dependencies [Hash] hash with the provisioned dependencies. def def_serializer(category = :default, &block) @serializer[category.to_sym] = block self end # Calls the `parser` of this attribute by passing `source` and resolved dependencies. # @note # - the method depenencies override keys of the _default dependencies_. # @raise [Exception] when there is **no** `parser` defined. # @param source [Any] source data to be parsed. # @param dependencies [Hash] _additional dependencies_ that should be merged to the _default dependencies_. def parse(source, category = :default, dependencies: {}) raise "There is no parser of type '#{category}' for this attribue '#{attr}'" unless parser_category?(category) call_block(source, @dependencies.merge(dependencies), attr, &@parser[category.to_sym]) end # Calls the `serializer` of this attribute by passing `object` and resolved dependencies. # @note # - the method depenencies override keys of the _default dependencies_. # @raise [Exception] when there is **no** `serializer` defined. # @param object [Any] source data to be serialized. # @param dependencies [Hash] _additional dependencies_ that should be merged to the _default dependencies_. def serialize(object, category = :default, dependencies: {}) raise "There is no serializer of type '#{category}' for this attribue '#{attr}'" unless serializer_category?(category) call_block(object, @dependencies.merge(dependencies), attr, &@serializer[category.to_sym]) end # Checks if there's a `parser` defined for `category` # @return [Boolean] `true` if the parser is defined, and `false` otherwise def parser_category?(category = :default) @parser.key?(category.to_sym) end # Checks if there's a `serializer` defined for `category` # @return [Boolean] `true` if the serializer is defined, and `false` otherwise def serializer_category?(category = :default) @serializer.key?(category.to_sym) end private # The methods may expect less parameters from some type of parsers. # Here, we ensure they are called with the expected number of parameters. def call_block(*args, &block) params = block.parameters.zip(args).map(&:last) yield(*params) end end end end end