Skip to content

Commit

Permalink
Support keyword arguments MutableStruct, ImmutableStruct, and Settabl…
Browse files Browse the repository at this point in the history
…eStruct
  • Loading branch information
numeraltwo committed Jul 7, 2018
1 parent 5610226 commit 4e0b166
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 21 deletions.
8 changes: 4 additions & 4 deletions lib/concurrent/immutable_struct.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,20 @@ def select(&block)
end

# @!macro struct_new
def self.new(*args, &block)
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, &block)
FACTORY.define_struct(clazz_name, args, kw_args, &block)
end

FACTORY = Class.new(Synchronization::LockableObject) do
def define_struct(name, members, &block)
def define_struct(name, members, kw_args, &block)
synchronize do
Synchronization::AbstractStruct.define_struct_class(ImmutableStruct, Synchronization::Object, name, members, &block)
Synchronization::AbstractStruct.define_struct_class(ImmutableStruct, Synchronization::Object, name, members, kw_args, &block)
end
end
end.new
Expand Down
8 changes: 4 additions & 4 deletions lib/concurrent/mutable_struct.rb
Original file line number Diff line number Diff line change
Expand Up @@ -197,20 +197,20 @@ def []=(member, value)
end

# @!macro struct_new
def self.new(*args, &block)
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, &block)
FACTORY.define_struct(clazz_name, args, kw_args, &block)
end

FACTORY = Class.new(Synchronization::LockableObject) do
def define_struct(name, members, &block)
def define_struct(name, members, kw_args, &block)
synchronize do
clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::LockableObject, name, members, &block)
clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::LockableObject, name, members, kw_args, &block)
members.each_with_index do |member, index|
clazz.send :remove_method, member
clazz.send(:define_method, member) do
Expand Down
8 changes: 4 additions & 4 deletions lib/concurrent/settable_struct.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,20 @@ def []=(member, value)
end

# @!macro struct_new
def self.new(*args, &block)
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, &block)
FACTORY.define_struct(clazz_name, args, kw_args, &block)
end

FACTORY = Class.new(Synchronization::LockableObject) do
def define_struct(name, members, &block)
def define_struct(name, members, kw_args, &block)
synchronize do
clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::LockableObject, name, members, &block)
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
Expand Down
22 changes: 16 additions & 6 deletions lib/concurrent/synchronization/abstract_struct.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,18 @@ module Synchronization
module AbstractStruct

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

# @!macro [attach] struct_keyword_init
#
# Returns `true` if the struct uses keyword arguments.
#
# @return [Boolean] true if struct uses keyword arguments
def keyword_init?
self.class::KEYWORD_INIT
end

# @!macro [attach] struct_length
Expand Down Expand Up @@ -127,13 +136,14 @@ def pr_underscore(clazz)
end

# @!visibility private
def self.define_struct_class(parent, base, name, members, &block)
def self.define_struct_class(parent, base, name, members, kw_args, &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)
self.const_set(:KEYWORD_INIT, !!kw_args[:keyword_init])
def ns_initialize(*values, **kw_values)
raise ArgumentError.new('struct size differs') if values.length > length || kw_values.length > length
@values = keyword_init? ? members.map{ |val| kw_values.fetch(val, nil) } : values.fill(nil, values.length..length-1)
end
end
unless name.nil?
Expand Down
28 changes: 25 additions & 3 deletions spec/concurrent/struct_shared.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
members = [:Foo, :bar, 'baz']
structs = [
described_class.new(*members).new,
described_class.new('ClassForCheckingGetterDefinition', *members).new
described_class.new(*members, keyword_init: true).new,
described_class.new('ClassForCheckingGetterDefinition', *members).new,
]

structs.each do |struct|
Expand All @@ -54,8 +55,11 @@ def baz(foo, bar) foo + bar; end
clazz2 = described_class.new(:foo, :bar) do
def baz(foo, bar) foo + bar; end
end
clazz3 = described_class.new(:foo, :bar, keyword_init: true) do
def baz(foo, bar) foo + bar; end
end

[clazz1, clazz2].each do |clazz|
[clazz1, clazz2, clazz3].each do |clazz|
struct = clazz.new
expect(struct).to respond_to :baz
expect(struct.method(:baz).arity).to eq 2
Expand All @@ -68,9 +72,11 @@ def baz(foo, bar) foo + bar; end

let!(:members){ [:Foo, :bar, 'baz'] }
let!(:values){ [42, '42', :fortytwo] }
let!(:kw_values){ {Foo: 42, bar: '42', baz: :fortytwo} }
let!(:classes) do
[
described_class.new(*members),
described_class.new(*members, keyword_init: true),
described_class.new('StructConstructionTester', *members)
]
end
Expand All @@ -86,7 +92,7 @@ def baz(foo, bar) foo + bar; end

it 'sets all given members in order' do
classes.each do |clazz|
struct = clazz.new(*values)
struct = clazz::KEYWORD_INIT ? clazz.new(**kw_values) : clazz.new(*values)
members.each_with_index do |member, index|
expect(struct.send(member)).to eq values[index]
end
Expand All @@ -105,6 +111,7 @@ def baz(foo, bar) foo + bar; end

let!(:anon_struct_members) { [:name, :address, :zip] }
let(:anon_struct) { described_class.new(*anon_struct_members) }
let(:anon_struct_with_keyword_init) { described_class.new(*anon_struct_members, keyword_init: true) }

let!(:named_struct_members) { [:left, :right] }
let(:named_struct) do
Expand All @@ -116,19 +123,31 @@ def baz(foo, bar) foo + bar; end

it 'returns the number of struct members' do
expect(anon_struct.new.length).to eq anon_struct_members.length
expect(anon_struct_with_keyword_init.new.length).to eq anon_struct_members.length
expect(named_struct.new.length).to eq named_struct_members.length
end
end

context '#keyword_init?' do

it 'returns a boolean indicating if keywords are enabled for initialization' do
expect(anon_struct.new.keyword_init?).to eq false
expect(anon_struct_with_keyword_init.new.keyword_init?).to eq true
expect(named_struct.new.keyword_init?).to eq false
end
end

context '#members' do

it 'returns the struct members as an array of symbols' do
expect(anon_struct.new.members).to eq anon_struct_members
expect(anon_struct_with_keyword_init.new.members).to eq anon_struct_members
expect(named_struct.new.members).to eq named_struct_members
end

it 'returns a different object than the array passed at definition' do
expect(anon_struct.new.members.object_id).to_not eq anon_struct_members.object_id
expect(anon_struct_with_keyword_init.new.members.object_id).to_not eq anon_struct_members.object_id
expect(named_struct.new.members.object_id).to_not eq named_struct_members.object_id
end
end
Expand All @@ -137,6 +156,7 @@ def baz(foo, bar) foo + bar; end

it 'returns the number of struct members' do
expect(anon_struct.new.size).to eq anon_struct_members.size
expect(anon_struct_with_keyword_init.new.size).to eq anon_struct_members.size
expect(named_struct.new.size).to eq named_struct_members.size
end
end
Expand All @@ -145,9 +165,11 @@ def baz(foo, bar) foo + bar; end

it 'returns the values of the struct as an array in order' do
expect(anon_struct.new().values).to eq [nil, nil, nil]
expect(anon_struct_with_keyword_init.new().values).to eq [nil, nil, nil]
expect(named_struct.new().values).to eq [nil, nil]

expect(anon_struct.new(:foo, :bar, :baz).values).to eq [:foo, :bar, :baz]
expect(anon_struct_with_keyword_init.new(name: :foo, address: :bar, zip: :baz).values).to eq [:foo, :bar, :baz]
expect(named_struct.new(:yes, :no).values).to eq [:yes, :no]
end
end
Expand Down

0 comments on commit 4e0b166

Please sign in to comment.