module Concurrent::Synchronization::AbstractStruct

def self.define_struct_class(parent, base, name, members, &block)

@!visibility private
def self.define_struct_class(parent, base, name, members, &block)
  clazz = Class.new(base || Object) do
    include parent
    self.const_set(:MEMBERS, members.collect{|member| member.to_s.to_sym}.freeze)
    def ns_initialize(*values)
      raise ArgumentError.new('struct size differs') if values.length > length
      @values = values.fill(nil, values.length..length-1)
    end
  end
  unless name.nil?
    begin
      parent.send :remove_const, name if parent.const_defined?(name, false)
      parent.const_set(name, clazz)
      clazz
    rescue NameError
      raise NameError.new("identifier #{name} needs to be constant")
    end
  end
  members.each_with_index do |member, index|
    clazz.send :remove_method, member if clazz.instance_methods.include? member
    clazz.send(:define_method, member) do
      @values[index]
    end
  end
  clazz.class_exec(&block) unless block.nil?
  clazz.singleton_class.send :alias_method, :[], :new
  clazz
end

def initialize(*values)

@!visibility private
def initialize(*values)
  super()
  ns_initialize(*values)
end

def length

Returns:
  • (Fixnum) - the number of struct members
def length
  self.class::MEMBERS.length
end

def members

Returns:
  • (Array) - the struct members as an array of symbols
def members
  self.class::MEMBERS.dup
end

def ns_each

@!visibility private

@!macro struct_each
def ns_each
  values.each{|value| yield value }
end

def ns_each_pair

@!visibility private

@!macro struct_each_pair
def ns_each_pair
  @values.length.times do |index|
    yield self.class::MEMBERS[index], @values[index]
  end
end

def ns_equality(other)

@!visibility private

@!macro struct_equality
def ns_equality(other)
  self.class == other.class && self.values == other.values
end

def ns_get(member)

@!visibility private

@!macro struct_get
def ns_get(member)
  if member.is_a? Integer
    if member >= @values.length
      raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})")
    end
    @values[member]
  else
    send(member)
  end
rescue NoMethodError
  raise NameError.new("no member '#{member}' in struct")
end

def ns_initialize(*values)

def ns_initialize(*values)
  raise ArgumentError.new('struct size differs') if values.length > length
  @values = values.fill(nil, values.length..length-1)
end

def ns_initialize_copy

@!visibility private
def ns_initialize_copy
  @values = @values.map do |val|
    begin
      val.clone
    rescue TypeError
      val
    end
  end
end

def ns_inspect

@!visibility private

@!macro struct_inspect
def ns_inspect
  struct = pr_underscore(self.class.ancestors[1])
  clazz = ((self.class.to_s =~ /^#<Class:/) == 0) ? '' : " #{self.class}"
  "#<#{struct}#{clazz} #{ns_to_h}>"
end

def ns_merge(other, &block)

@!visibility private

@!macro struct_merge
def ns_merge(other, &block)
  self.class.new(*self.to_h.merge(other, &block).values)
end

def ns_select

@!visibility private

@!macro struct_select
def ns_select
  values.select{|value| yield value }
end

def ns_to_h

@!visibility private

@!macro struct_to_h
def ns_to_h
  length.times.reduce({}){|memo, i| memo[self.class::MEMBERS[i]] = @values[i]; memo}
end

def ns_values

@!visibility private

@!macro struct_values
def ns_values
  @values.dup
end

def ns_values_at(indexes)

@!visibility private

@!macro struct_values_at
def ns_values_at(indexes)
  @values.values_at(*indexes)
end

def pr_underscore(clazz)

@!visibility private
def pr_underscore(clazz)
  word = clazz.to_s.dup # dup string to workaround JRuby 9.2.0.0 bug https://github.com/jruby/jruby/issues/5229
  word.gsub!(/::/, '/')
  word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
  word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
  word.tr!("-", "_")
  word.downcase!
  word
end