1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
require 'concurrent/synchronization/abstract_struct'
require 'concurrent/errors'
require 'concurrent/synchronization'
module Concurrent
# An thread-safe, write-once variation of Ruby's standard `Struct`.
# Each member can have its value set at most once, either at construction
# or any time thereafter. Attempting to assign a value to a member
# that has already been set will result in a `Concurrent::ImmutabilityError`.
#
# @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct`
# @see http://en.wikipedia.org/wiki/Final_(Java) Java `final` keyword
module SettableStruct
include Synchronization::AbstractStruct
# @!macro struct_values
def values
synchronize { ns_values }
end
alias_method :to_a, :values
# @!macro struct_values_at
def values_at(*indexes)
synchronize { ns_values_at(indexes) }
end
# @!macro struct_inspect
def inspect
synchronize { ns_inspect }
end
alias_method :to_s, :inspect
# @!macro struct_merge
def merge(other, &block)
synchronize { ns_merge(other, &block) }
end
# @!macro struct_to_h
def to_h
synchronize { ns_to_h }
end
# @!macro struct_get
def [](member)
synchronize { ns_get(member) }
end
# @!macro struct_equality
def ==(other)
synchronize { ns_equality(other) }
end
# @!macro struct_each
def each(&block)
return enum_for(:each) unless block_given?
synchronize { ns_each(&block) }
end
# @!macro struct_each_pair
def each_pair(&block)
return enum_for(:each_pair) unless block_given?
synchronize { ns_each_pair(&block) }
end
# @!macro struct_select
def select(&block)
return enum_for(:select) unless block_given?
synchronize { ns_select(&block) }
end
# @!macro struct_set
#
# @raise [Concurrent::ImmutabilityError] if the given member has already been set
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 do
unless @values[member].nil?
raise Concurrent::ImmutabilityError.new('struct member has already been set')
end
@values[member] = value
end
else
send("#{member}=", value)
end
rescue NoMethodError
raise NameError.new("no member '#{member}' in struct")
end
private
# @!visibility private
def initialize_copy(original)
synchronize do
super(original)
ns_initialize_copy
end
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(SettableStruct, Synchronization::LockableObject, name, members, &block)
members.each_with_index do |member, index|
clazz.send :remove_method, member if clazz.instance_methods.include? member
clazz.send(:define_method, member) do
synchronize { @values[index] }
end
clazz.send(:define_method, "#{member}=") do |value|
synchronize do
unless @values[index].nil?
raise Concurrent::ImmutabilityError.new('struct member has already been set')
end
@values[index] = value
end
end
end
clazz
end
end
end.new
private_constant :FACTORY
end
end
|