rf-web/vendor/bundle/gems/concurrent-ruby-1.1.5/lib/concurrent/mutable_struct.rb

230 lines
8.4 KiB
Ruby
Raw Normal View History

2019-10-21 08:18:17 +00:00
require 'concurrent/synchronization/abstract_struct'
require 'concurrent/synchronization'
module Concurrent
# An thread-safe variation of Ruby's standard `Struct`. Values can be set at
# construction or safely changed at any time during the object's lifecycle.
#
# @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct`
module MutableStruct
include Synchronization::AbstractStruct
# @!macro struct_new
#
# Factory for creating new struct classes.
#
# ```
# new([class_name] [, member_name]+>) -> StructClass click to toggle source
# new([class_name] [, member_name]+>) {|StructClass| block } -> StructClass
# new(value, ...) -> obj
# StructClass[value, ...] -> obj
# ```
#
# The first two forms are used to create a new struct subclass `class_name`
# that can contain a value for each member_name . This subclass can be
# used to create instances of the structure like any other Class .
#
# If the `class_name` is omitted an anonymous struct class will be created.
# Otherwise, the name of this struct will appear as a constant in the struct class,
# so it must be unique for all structs under this base class and must start with a
# capital letter. Assigning a struct class to a constant also gives the class
# the name of the constant.
#
# If a block is given it will be evaluated in the context of `StructClass`, passing
# the created class as a parameter. This is the recommended way to customize a struct.
# Subclassing an anonymous struct creates an extra anonymous class that will never be used.
#
# The last two forms create a new instance of a struct subclass. The number of value
# parameters must be less than or equal to the number of attributes defined for the
# struct. Unset parameters default to nil. Passing more parameters than number of attributes
# will raise an `ArgumentError`.
#
# @see http://ruby-doc.org/core-2.2.0/Struct.html#method-c-new Ruby standard library `Struct#new`
# @!macro struct_values
#
# Returns the values for this struct as an Array.
#
# @return [Array] the values for this struct
#
def values
synchronize { ns_values }
end
alias_method :to_a, :values
# @!macro struct_values_at
#
# Returns the struct member values for each selector as an Array.
#
# A selector may be either an Integer offset or a Range of offsets (as in `Array#values_at`).
#
# @param [Fixnum, Range] indexes the index(es) from which to obatin the values (in order)
def values_at(*indexes)
synchronize { ns_values_at(indexes) }
end
# @!macro struct_inspect
#
# Describe the contents of this struct in a string.
#
# @return [String] the contents of this struct in a string
def inspect
synchronize { ns_inspect }
end
alias_method :to_s, :inspect
# @!macro struct_merge
#
# Returns a new struct containing the contents of `other` and the contents
# of `self`. If no block is specified, the value for entries with duplicate
# keys will be that of `other`. Otherwise the value for each duplicate key
# is determined by calling the block with the key, its value in `self` and
# its value in `other`.
#
# @param [Hash] other the hash from which to set the new values
# @yield an options block for resolving duplicate keys
# @yieldparam [String, Symbol] member the name of the member which is duplicated
# @yieldparam [Object] selfvalue the value of the member in `self`
# @yieldparam [Object] othervalue the value of the member in `other`
#
# @return [Synchronization::AbstractStruct] a new struct with the new values
#
# @raise [ArgumentError] of given a member that is not defined in the struct
def merge(other, &block)
synchronize { ns_merge(other, &block) }
end
# @!macro struct_to_h
#
# Returns a hash containing the names and values for the structs members.
#
# @return [Hash] the names and values for the structs members
def to_h
synchronize { ns_to_h }
end
# @!macro struct_get
#
# Attribute Reference
#
# @param [Symbol, String, Integer] member the string or symbol name of the member
# for which to obtain the value or the member's index
#
# @return [Object] the value of the given struct member or the member at the given index.
#
# @raise [NameError] if the member does not exist
# @raise [IndexError] if the index is out of range.
def [](member)
synchronize { ns_get(member) }
end
# @!macro struct_equality
#
# Equality
#
# @return [Boolean] true if other has the same struct subclass and has
# equal member values (according to `Object#==`)
def ==(other)
synchronize { ns_equality(other) }
end
# @!macro struct_each
#
# Yields the value of each struct member in order. If no block is given
# an enumerator is returned.
#
# @yield the operation to be performed on each struct member
# @yieldparam [Object] value each struct value (in order)
def each(&block)
return enum_for(:each) unless block_given?
synchronize { ns_each(&block) }
end
# @!macro struct_each_pair
#
# Yields the name and value of each struct member in order. If no block is
# given an enumerator is returned.
#
# @yield the operation to be performed on each struct member/value pair
# @yieldparam [Object] member each struct member (in order)
# @yieldparam [Object] value each struct value (in order)
def each_pair(&block)
return enum_for(:each_pair) unless block_given?
synchronize { ns_each_pair(&block) }
end
# @!macro struct_select
#
# Yields each member value from the struct to the block and returns an Array
# containing the member values from the struct for which the given block
# returns a true value (equivalent to `Enumerable#select`).
#
# @yield the operation to be performed on each struct member
# @yieldparam [Object] value each struct value (in order)
#
# @return [Array] an array containing each value for which the block returns true
def select(&block)
return enum_for(:select) unless block_given?
synchronize { ns_select(&block) }
end
# @!macro struct_set
#
# Attribute Assignment
#
# Sets the value of the given struct member or the member at the given index.
#
# @param [Symbol, String, Integer] member the string or symbol name of the member
# for which to obtain the value or the member's index
#
# @return [Object] the value of the given struct member or the member at the given index.
#
# @raise [NameError] if the name does not exist
# @raise [IndexError] if the index is out of range.
def []=(member, value)
if member.is_a? Integer
length = synchronize { @values.length }
if member >= length
raise IndexError.new("offset #{member} too large for struct(size:#{length})")
end
synchronize { @values[member] = value }
else
send("#{member}=", value)
end
rescue NoMethodError
raise NameError.new("no member '#{member}' in struct")
end
# @!macro struct_new
def self.new(*args, &block)
clazz_name = nil
if args.length == 0
raise ArgumentError.new('wrong number of arguments (0 for 1+)')
elsif args.length > 0 && args.first.is_a?(String)
clazz_name = args.shift
end
FACTORY.define_struct(clazz_name, args, &block)
end
FACTORY = Class.new(Synchronization::LockableObject) do
def define_struct(name, members, &block)
synchronize do
clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::LockableObject, name, members, &block)
members.each_with_index do |member, index|
clazz.send :remove_method, member
clazz.send(:define_method, member) do
synchronize { @values[index] }
end
clazz.send(:define_method, "#{member}=") do |value|
synchronize { @values[index] = value }
end
end
clazz
end
end
end.new
private_constant :FACTORY
end
end