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
140
141
142
143
144
145
146
147
148
149
150
|
require_relative '../spec_helper'
# Should be synchronized with spec/ruby/language/optional_assignments_spec.rb
describe 'Assignments' do
describe 'using +=' do
describe 'using an accessor' do
before do
klass = Class.new { attr_accessor :b }
@a = klass.new
end
it 'does evaluate receiver only once when assigns' do
ScratchPad.record []
@a.b = 1
(ScratchPad << :evaluated; @a).b += 2
ScratchPad.recorded.should == [:evaluated]
@a.b.should == 3
end
it 'ignores method visibility when receiver is self' do
klass_with_private_methods = Class.new do
def initialize(n) @a = n end
def public_method(n); self.a += n end
private
def a; @a end
def a=(n) @a = n; 42 end
end
a = klass_with_private_methods.new(0)
a.public_method(2).should == 2
end
end
describe 'using a #[]' do
before do
klass = Class.new do
def [](k)
@hash ||= {}
@hash[k]
end
def []=(k, v)
@hash ||= {}
@hash[k] = v
7
end
end
@b = klass.new
end
it 'evaluates receiver only once when assigns' do
ScratchPad.record []
a = {k: 1}
(ScratchPad << :evaluated; a)[:k] += 2
ScratchPad.recorded.should == [:evaluated]
a[:k].should == 3
end
it 'ignores method visibility when receiver is self' do
klass_with_private_methods = Class.new do
def initialize(h) @a = h end
def public_method(k, n); self[k] += n end
private
def [](k) @a[k] end
def []=(k, v) @a[k] = v; 42 end
end
a = klass_with_private_methods.new(k: 0)
a.public_method(:k, 2).should == 2
end
context 'splatted argument' do
it 'correctly handles it' do
@b[:m] = 10
(@b[*[:m]] += 10).should == 20
@b[:m].should == 20
@b[:n] = 10
(@b[*(1; [:n])] += 10).should == 20
@b[:n].should == 20
@b[:k] = 10
(@b[*begin 1; [:k] end] += 10).should == 20
@b[:k].should == 20
end
it 'calls #to_a only once' do
k = Object.new
def k.to_a
ScratchPad << :to_a
[:k]
end
ScratchPad.record []
@b[:k] = 10
(@b[*k] += 10).should == 20
@b[:k].should == 20
ScratchPad.recorded.should == [:to_a]
end
it 'correctly handles a nested splatted argument' do
@b[:k] = 10
(@b[*[*[:k]]] += 10).should == 20
@b[:k].should == 20
end
it 'correctly handles multiple nested splatted arguments' do
klass_with_multiple_parameters = Class.new do
def [](k1, k2, k3)
@hash ||= {}
@hash[:"#{k1}#{k2}#{k3}"]
end
def []=(k1, k2, k3, v)
@hash ||= {}
@hash[:"#{k1}#{k2}#{k3}"] = v
7
end
end
a = klass_with_multiple_parameters.new
a[:a, :b, :c] = 10
(a[*[:a], *[:b], *[:c]] += 10).should == 20
a[:a, :b, :c].should == 20
end
end
end
describe 'using compounded constants' do
it 'causes side-effects of the module part to be applied only once (when assigns)' do
module ConstantSpecs
OpAssignTrue = 1
end
suppress_warning do # already initialized constant
x = 0
(x += 1; ConstantSpecs)::OpAssignTrue += 2
x.should == 1
ConstantSpecs::OpAssignTrue.should == 3
end
ConstantSpecs.send :remove_const, :OpAssignTrue
end
end
end
end
|