forked from ruby-concurrency/concurrent-ruby
-
Notifications
You must be signed in to change notification settings - Fork 0
/
settable_struct.rb
129 lines (114 loc) · 3.79 KB
/
settable_struct.rb
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
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
# @!macro struct_new
def self.new(*args, **kw_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, kw_args, &block)
end
FACTORY = Class.new(Synchronization::LockableObject) do
def define_struct(name, members, kw_args, &block)
synchronize do
clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::LockableObject, name, members, kw_args, &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