module Origen
module Utility
class Collector
# Returns the collector's hash. This is the same as calling {#to_h}
attr_reader :_hash_
# Queries the current merge_method.
attr_reader :merge_method
# Need to keep a seperate methods list so we know what's been added by method missing instead of what's
# been added either by the hash or by method missing.
# Only overwriting a value in the block should cause an error. Overriding a value from the hash depends on
# the merge method's setting.
# List of the currently seen method names.
attr_reader :_methods_
# Creates a new Collector object and creates a Hash out of the methods names and values in the given block.
# @see https://origen-sdk.org/origen/guides/misc/utilities/#Collector
# @example Create a collector to transform a block into a Hash
# Origen::Utility::Collector.new { |c| c.my_param 'My Parameter'}.to_h #=> {my_param: 'My Parameter'}
# @yield [self] Passes the collector to the given block.
# @param options [Hash] Customization options.
# @option options [Hash] :hash Input, or starting, values that are set in the output hash prior to calling the given block.
# @option options [Symbol] :merge_method (:keep_hash) Indicates how arguments that exist in both the input hash and in the block should be handled.
# Accpeted values are :keep_hash, :keep_block, :fail.
# @raise [Origen::OrigenError] Raised when an unknown merge method is used.
def initialize(options = {}, &block)
@merge_method = options[:merge_method] || :keep_hash
@fail_on_empty_args = options[:fail_on_empty_args]
unless [:keep_hash, :keep_block, :fail].include?(@merge_method)
fail Origen::OrigenError, "Origen::Utility::Collector cannot merge with method :#{@merge_method} (of class #{@merge_method.class}). Known merge methods are :keep_hash (default), :keep_block, or :fail"
end
@_hash_ = options.key?(:hash) ? options[:hash].clone : {}
@_methods_ = []
if block_given?
yield self
end
end
# Retrieve the collector's hash.
# @deprecated Use Ruby-centric {#to_hash} instead.
# @return [Hash] Hash representation of the collector.
def store
Origen.log.deprecate 'Collector::store method was used. Please use the Ruby-centric Collector::to_h or Collector::to_hash method instead' \
" Called from: #{caller[0]}"
@_hash_
end
# Returns the collector, as a Hash.
# @return [Hash] Hash representation of the collector.
def to_hash
@_hash_
end
alias_method :to_h, :to_hash
# Using the method name, creates a key in the Collector with argument given to the method.
# @see https://origen-sdk.org/origen/guides/misc/utilities/#Collector
# @note If no args are given, the method key is set to nil
.
# @raise [ArgumentError] Raised when a method attempts to use both arguments and a block in the same line.
# E.g.: collector.my_param 'my_param' { 'MyParam' }
# @raise [ArgumentError] Raised when a method attempts to use multiple input values.
# E.g.: collector.my_param 'my_param', 'MyParam'
# @raise [Origen::OrigenError] Raised when a method is set more than once. I.e., overriding values are not allowed.
# E.g.: collector.my_param 'my_param'; collector.my_param 'MyParam'
# @raise [Origen::OrigenError] Raised when the input hash and the block attempt to set the same method name and the merge method is set to :fail
.
def method_missing(method, *args, &_block)
key = method.to_s.sub('=', '').to_sym
# Check that the arguments are correct
if block_given? && !args.empty?
# raise Origen::OrigenError, "Origen::Utility::Collector detected both the hash and block attempting to set :#{key} (merge_method set to :fail)"
fail ArgumentError, "Origen::Utility::Collector cannot accept both an argument list and block simultaneously for :#{key}. Please use one or the other."
elsif block_given?
val = _block
elsif args.size == 0
# Set any empty argument to nil
val = nil
elsif args.size > 1
fail ArgumentError, "Origen::Utility::Collector does not allow method :#{key} more than 1 argument. Received 3 arguments."
else
val = args.first
end
# Check if we've already added this key via a method
if _methods_.include?(key)
fail Origen::OrigenError, "Origen::Utility::Collector does not allow method :#{key} to be set more than a single time. :#{key} is set to #{_hash_[key]}, tried to set it again to #{val}"
end
# indicate that we've seen this method, and decide whether or not to add the new value
_methods_ << key
# Merge the value (or don't, depending on what is set)
if merge_method == :keep_block || !_hash_.key?(key)
_hash_[key] = val
elsif merge_method == :fail
fail Origen::OrigenError, "Origen::Utility::Collector detected both the hash and block attempting to set :#{key} (merge_method set to :fail)"
end
# store[key] = val if !store.key?(key) || (store.key?(key) && merge_method == :keep_block)
# Return self instead of the key value to allow for one-line collector statements
self
end
end
end
end