spec/language/variables_spec.rb in opal-0.3.41 vs spec/language/variables_spec.rb in opal-0.3.42
- old
+ new
@@ -1,5 +1,10 @@
+require File.expand_path('../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/variables', __FILE__)
+
+# TODO: partition these specs into distinct cases based on the
+# real parsed forms, not the superficial code forms.
describe "Basic assignment" do
it "allows the rhs to be assigned to the lhs" do
a = nil
a.should == nil
end
@@ -7,25 +12,92 @@
it "assigns nil to lhs when rhs is an empty expression" do
a = ()
a.should be_nil
end
- it "assigns [] to lhs when rhs is an empty splat expression" do
- a = *()
- a.should == []
+ ruby_version_is "" ... "1.9" do
+ it "assigns nil to lhs when rhs is an empty splat expression" do
+ a = *()
+ a.should be_nil
+ end
end
- it "allows the assignment of the rhs to the lhs using the rhs splat operator" do
- a = *nil; a.should == []
- a = *1; a.should == [1]
- a = *[]; a.should == []
- a = *[1]; a.should == [1]
- a = *[nil]; a.should == [nil]
- a = *[[]]; a.should == [[]]
- a = *[1,2]; a.should == [1,2]
+ ruby_version_is "1.9" do
+ it "assigns [] to lhs when rhs is an empty splat expression" do
+ a = *()
+ a.should == []
+ end
end
+ ruby_version_is "" ... "1.9" do
+ it "allows the assignment of the rhs to the lhs using the rhs splat operator" do
+ a = *nil; a.should == nil
+ a = *1; a.should == 1
+ a = *[]; a.should == nil
+ a = *[1]; a.should == 1
+ a = *[nil]; a.should == nil
+ a = *[[]]; a.should == []
+ a = *[1,2]; a.should == [1,2]
+ end
+ end
+
+ ruby_version_is "1.9" do
+ it "allows the assignment of the rhs to the lhs using the rhs splat operator" do
+ a = *nil; a.should == []
+ a = *1; a.should == [1]
+ a = *[]; a.should == []
+ a = *[1]; a.should == [1]
+ a = *[nil]; a.should == [nil]
+ a = *[[]]; a.should == [[]]
+ a = *[1,2]; a.should == [1,2]
+ end
+ end
+
+ ruby_version_is "" ... "1.9" do
+ it "allows the assignment of the rhs to the lhs using the lhs splat operator" do
+ # * = 1,2 # Valid syntax, but pretty useless! Nothing to test
+ *a = nil; a.should == [nil]
+ *a = 1; a.should == [1]
+ *a = []; a.should == [[]]
+ *a = [1]; a.should == [[1]]
+ *a = [1,2]; a.should == [[1,2]]
+ end
+ end
+
+ ruby_version_is "1.9" do
+ pending "allows the assignment of the rhs to the lhs using the lhs splat operator" do
+ # * = 1,2 # Valid syntax, but pretty useless! Nothing to test
+ *a = nil; a.should == [nil]
+ *a = 1; a.should == [1]
+ *a = []; a.should == []
+ *a = [1]; a.should == [1]
+ *a = [1,2]; a.should == [1,2]
+ end
+ end
+
+ ruby_version_is "" ... "1.9" do
+ it "allows the assignment of rhs to the lhs using the lhs and rhs splat operators simultaneously" do
+ *a = *nil; a.should == [nil]
+ *a = *1; a.should == [1]
+ *a = *[]; a.should == []
+ *a = *[1]; a.should == [1]
+ *a = *[nil]; a.should == [nil]
+ *a = *[1,2]; a.should == [1,2]
+ end
+ end
+
+ ruby_version_is "1.9" do
+ pending "allows the assignment of rhs to the lhs using the lhs and rhs splat operators simultaneously" do
+ *a = *nil; a.should == []
+ *a = *1; a.should == [1]
+ *a = *[]; a.should == []
+ *a = *[1]; a.should == [1]
+ *a = *[nil]; a.should == [nil]
+ *a = *[1,2]; a.should == [1,2]
+ end
+ end
+
it "sets unavailable values to nil" do
ary = []
a, b, c = ary
a.should be_nil
@@ -40,26 +112,43 @@
a.should be_nil
b.should be_nil
c.should == []
end
- it "allows multiple values to be assigned" do
+ pending "allows multiple values to be assigned" do
a,b,*c = nil; [a,b,c].should == [nil, nil, []]
a,b,*c = 1; [a,b,c].should == [1, nil, []]
a,b,*c = []; [a,b,c].should == [nil, nil, []]
a,b,*c = [1]; [a,b,c].should == [1, nil, []]
a,b,*c = [nil]; [a,b,c].should == [nil, nil, []]
a,b,*c = [[]]; [a,b,c].should == [[], nil, []]
a,b,*c = [1,2]; [a,b,c].should == [1,2,[]]
a,b,*c = *nil; [a,b,c].should == [nil, nil, []]
+ a,b,*c = *1; [a,b,c].should == [1, nil, []]
a,b,*c = *[]; [a,b,c].should == [nil, nil, []]
+ a,b,*c = *[1]; [a,b,c].should == [1, nil, []]
a,b,*c = *[nil]; [a,b,c].should == [nil, nil, []]
a,b,*c = *[[]]; [a,b,c].should == [[], nil, []]
a,b,*c = *[1,2]; [a,b,c].should == [1,2,[]]
end
+ pending "calls to_a on the given argument when using a splat" do
+ a,b = *VariablesSpecs::ArrayLike.new([1,2]); [a,b].should == [1,2]
+ end
+
+ it "supports the {|r,| } form of block assignment" do
+ f = lambda {|r,| r.should == []}
+ f.call([], *[])
+
+ f = lambda{|x,| x}
+ f.call(42).should == 42
+ f.call([42]).should == [42]
+ f.call([[42]]).should == [[42]]
+ f.call([42,55]).should == [42,55]
+ end
+
it "allows assignment through lambda" do
f = lambda {|r,*l| r.should == []; l.should == [1]}
f.call([], *[1])
f = lambda{|x| x}
@@ -84,35 +173,254 @@
a.should == 15
end
end
describe "Assignment using expansion" do
+ ruby_version_is "" ... "1.9" do
+ it "succeeds without conversion" do
+ *x = (1..7).to_a
+ x.should == [[1, 2, 3, 4, 5, 6, 7]]
+ end
+ end
+
ruby_version_is "1.9" do
it "succeeds without conversion" do
*x = (1..7).to_a
x.should == [1, 2, 3, 4, 5, 6, 7]
end
end
end
+describe "Basic multiple assignment" do
+ describe "with a single RHS value" do
+ pending "does not call #to_ary on an Array instance" do
+ x = [1, 2]
+ x.should_not_receive(:to_ary)
+
+ a, b = x
+ a.should == 1
+ b.should == 2
+ end
+
+ pending "does not call #to_a on an Array instance" do
+ x = [1, 2]
+ x.should_not_receive(:to_a)
+
+ a, b = x
+ a.should == 1
+ b.should == 2
+ end
+
+ pending "does not call #to_ary on an Array subclass instance" do
+ x = VariablesSpecs::ArraySubclass.new [1, 2]
+ x.should_not_receive(:to_ary)
+
+ a, b = x
+ a.should == 1
+ b.should == 2
+ end
+
+ pending "does not call #to_a on an Array subclass instance" do
+ x = VariablesSpecs::ArraySubclass.new [1, 2]
+ x.should_not_receive(:to_a)
+
+ a, b = x
+ a.should == 1
+ b.should == 2
+ end
+
+ pending "calls #to_ary on an object" do
+ x = mock("single rhs value for masgn")
+ x.should_receive(:to_ary).and_return([1, 2])
+
+ a, b = x
+ a.should == 1
+ b.should == 2
+ end
+
+ pending "does not call #to_a on an object if #to_ary is not defined" do
+ x = mock("single rhs value for masgn")
+ x.should_not_receive(:to_a)
+
+ a, b = x
+ a.should == x
+ b.should be_nil
+ end
+
+ it "does not call #to_a on a String" do
+ x = "one\ntwo"
+
+ a, b = x
+ a.should == x
+ b.should be_nil
+ end
+ end
+
+ describe "with a splatted single RHS value" do
+ pending "does not call #to_ary on an Array instance" do
+ x = [1, 2]
+ x.should_not_receive(:to_ary)
+
+ a, b = *x
+ a.should == 1
+ b.should == 2
+ end
+
+ pending "does not call #to_a on an Array instance" do
+ x = [1, 2]
+ x.should_not_receive(:to_a)
+
+ a, b = *x
+ a.should == 1
+ b.should == 2
+ end
+
+ pending "does not call #to_ary on an Array subclass instance" do
+ x = VariablesSpecs::ArraySubclass.new [1, 2]
+ x.should_not_receive(:to_ary)
+
+ a, b = *x
+ a.should == 1
+ b.should == 2
+ end
+
+ pending "does not call #to_a on an Array subclass instance" do
+ x = VariablesSpecs::ArraySubclass.new [1, 2]
+ x.should_not_receive(:to_a)
+
+ a, b = *x
+ a.should == 1
+ b.should == 2
+ end
+
+ pending "calls #to_a on an object if #to_ary is not defined" do
+ x = mock("single splatted rhs value for masgn")
+ x.should_receive(:to_a).and_return([1, 2])
+
+ a, b = *x
+ a.should == 1
+ b.should == 2
+ end
+
+ ruby_version_is ""..."1.9" do
+ it "calls #to_ary on an object" do
+ x = mock("single splatted rhs value for masgn")
+ x.should_receive(:to_ary).and_return([1, 2])
+
+ a, b = *x
+ a.should == 1
+ b.should == 2
+ end
+
+ it "calls #to_a on a String" do
+ x = "one\ntwo"
+
+ a, b = *x
+ a.should == "one\n"
+ b.should == "two"
+ end
+ end
+
+ ruby_version_is "1.9" do
+ pending "does not call #to_ary on an object" do
+ x = mock("single splatted rhs value for masgn")
+ x.should_not_receive(:to_ary)
+
+ a, b = *x
+ a.should == x
+ b.should be_nil
+ end
+
+ pending "does not call #to_a on a String" do
+ x = "one\ntwo"
+
+ a, b = *x
+ a.should == x
+ b.should be_nil
+ end
+ end
+ end
+end
+
describe "Assigning multiple values" do
- it "allows parallel assignment" do
+ pending "allows parallel assignment" do
a, b = 1, 2
a.should == 1
b.should == 2
- a, = 1,2
+ # a, = 1,2
a.should == 1
end
it "allows safe parallel swapping" do
a, b = 1, 2
a, b = b, a
a.should == 2
b.should == 1
end
+ pending do
+ not_compliant_on :rubinius do
+ pending "returns the rhs values used for assignment as an array" do
+ # x = begin; a, b, c = 1, 2, 3; end
+ x.should == [1,2,3]
+ end
+ end
+ end
+
+ ruby_version_is "" ... "1.9" do
+ it "wraps a single value in an Array" do
+ *a = 1
+ a.should == [1]
+
+ b = [1]
+ *a = b
+ a.should == [b]
+ end
+ end
+
+ ruby_version_is "1.9" do
+ it "wraps a single value in an Array if it's not already one" do
+ *a = 1
+ a.should == [1]
+
+ b = [1]
+ *a = b
+ a.should == b
+ end
+ end
+
+ it "evaluates rhs left-to-right" do
+ a = VariablesSpecs::ParAsgn.new
+ d, e ,f = a.inc, a.inc, a.inc
+ d.should == 1
+ e.should == 2
+ f.should == 3
+ end
+
+ pending "supports parallel assignment to lhs args via object.method=" do
+ a = VariablesSpecs::ParAsgn.new
+ a.x, b = 1, 2
+
+ a.x.should == 1
+ b.should == 2
+
+ c = VariablesSpecs::ParAsgn.new
+ c.x, a.x = a.x, b
+
+ c.x.should == 1
+ a.x.should == 2
+ end
+
+ pending "supports parallel assignment to lhs args using []=" do
+ a = [1,2,3]
+ a[3], b = 4,5
+
+ a.should == [1,2,3,4]
+ b.should == 5
+ end
+
it "bundles remaining values to an array when using the splat operator" do
a, *b = 1, 2, 3
a.should == 1
b.should == [2, 3]
@@ -126,12 +434,665 @@
a.should == [nil]
a, = *[1]
a.should == 1
end
+
+ ruby_version_is ""..."1.9" do
+ it "calls #to_ary on rhs arg if rhs has only a single arg" do
+ x = VariablesSpecs::ParAsgn.new
+ a,b,c = x
+ a.should == 1
+ b.should == 2
+ c.should == 3
+
+ a,b,c = x,5
+ a.should == x
+ b.should == 5
+ c.should == nil
+
+ a,b,c = 5,x
+ a.should == 5
+ b.should == x
+ c.should == nil
+
+ a,b,*c = x,5
+ a.should == x
+ b.should == 5
+ c.should == []
+
+ # a,(b,c) = 5,x
+ a.should == 5
+ b.should == 1
+ c.should == 2
+
+ # a,(b,*c) = 5,x
+ a.should == 5
+ b.should == 1
+ c.should == [2,3,4]
+
+ # a,(b,(*c)) = 5,x
+ a.should == 5
+ b.should == 1
+ c.should == [2]
+
+ # a,(b,(*c),(*d)) = 5,x
+ a.should == 5
+ b.should == 1
+ c.should == [2]
+ d.should == [3]
+
+ # a,(b,(*c),(d,*e)) = 5,x
+ a.should == 5
+ b.should == 1
+ c.should == [2]
+ d.should == 3
+ e.should == []
+ end
+ end
+
+ ruby_version_is "1.9" do
+ pending "calls #to_ary on RHS arg if the corresponding LHS var is a splat" do
+ x = VariablesSpecs::ParAsgn.new
+
+ # a,(*b),c = 5,x
+ a.should == 5
+ b.should == x.to_ary
+ c.should == nil
+ end
+ end
+
+ ruby_version_is ""..."1.9" do
+ it "doen't call #to_ary on RHS arg when the corresponding LHS var is a splat" do
+ x = VariablesSpecs::ParAsgn.new
+
+ # a,(*b),c = 5,x
+ a.should == 5
+ b.should == [x]
+ c.should == nil
+ end
+ end
+
+ pending "allows complex parallel assignment" do
+ # a, (b, c), d = 1, [2, 3], 4
+ a.should == 1
+ b.should == 2
+ c.should == 3
+ d.should == 4
+
+ # x, (y, z) = 1, 2, 3
+ [x,y,z].should == [1,2,nil]
+ # x, (y, z) = 1, [2,3]
+ [x,y,z].should == [1,2,3]
+ # x, (y, z) = 1, [2]
+ [x,y,z].should == [1,2,nil]
+
+ # a,(b,c,*d),(e,f),*g = 0,[1,2,3,4],[5,6],7,8
+ a.should == 0
+ b.should == 1
+ c.should == 2
+ d.should == [3,4]
+ e.should == 5
+ f.should == 6
+ g.should == [7,8]
+
+ x = VariablesSpecs::ParAsgn.new
+ # a,(b,c,*d),(e,f),*g = 0,x,[5,6],7,8
+ a.should == 0
+ b.should == 1
+ c.should == 2
+ d.should == [3,4]
+ e.should == 5
+ f.should == 6
+ g.should == [7,8]
+ end
+
+ pending "allows a lhs arg to be used in another lhs args parallel assignment" do
+ c = [4,5,6]
+ a,b,c[a] = 1,2,3
+ a.should == 1
+ b.should == 2
+ c.should == [4,3,6]
+
+ c[a],b,a = 7,8,9
+ a.should == 9
+ b.should == 8
+ c.should == [4,7,6]
+ end
end
+describe "Conditional assignment" do
+ it "assigns the lhs if previously unassigned" do
+ a=[]
+ a[0] ||= "bar"
+ a[0].should == "bar"
+
+ h={}
+ h["foo"] ||= "bar"
+ h["foo"].should == "bar"
+
+ h["foo".to_sym] ||= "bar"
+ h["foo".to_sym].should == "bar"
+
+ aa = 5
+ aa ||= 25
+ aa.should == 5
+
+ bb ||= 25
+ bb.should == 25
+
+ cc &&=33
+ cc.should == nil
+
+ cc = 5
+ cc &&=44
+ cc.should == 44
+ end
+
+ it "checks for class variable definition before fetching its value" do
+ class VariableSpecCVarSpec
+ @@cvarspec ||= 5
+ @@cvarspec.should == 5
+ end
+ end
+end
+
+describe "Unconditional operator assignment 'var op= expr'" do
+ it "is equivalent to 'var = var op expr'" do
+ x = 13
+ (x += 5).should == 18
+ x.should == 18
+
+ x = 17
+ (x -= 11).should == 6
+ x.should == 6
+
+ x = 2
+ (x *= 5).should == 10
+ x.should == 10
+
+ x = 36
+ (x /= 9).should == 4
+ x.should == 4
+
+ x = 23
+ (x %= 5).should == 3
+ x.should == 3
+ (x %= 3).should == 0
+ x.should == 0
+
+ x = 2
+ (x **= 3).should == 8
+ x.should == 8
+
+ x = 4
+ (x |= 3).should == 7
+ x.should == 7
+ (x |= 4).should == 7
+ x.should == 7
+
+ x = 6
+ (x &= 3).should == 2
+ x.should == 2
+ (x &= 4).should == 0
+ x.should == 0
+
+ # XOR
+ x = 2
+ (x ^= 3).should == 1
+ x.should == 1
+ (x ^= 4).should == 5
+ x.should == 5
+
+ # Bit-shift left
+ x = 17
+ (x <<= 3).should == 136
+ x.should == 136
+
+ # Bit-shift right
+ x = 5
+ (x >>= 1).should == 2
+ x.should == 2
+ end
+end
+
+describe "Conditional operator assignment 'var op= expr'" do
+ it "assigns the lhs if its truthiness is false for ||, true for &&" do
+ x = nil
+ (x ||= 17).should == 17
+ x.should == 17
+ (x ||= 2).should == 17
+ x.should == 17
+
+ x = false
+ (x &&= true).should == false
+ x.should == false
+ (x &&= false).should == false
+ x.should == false
+ x = true
+ (x &&= true).should == true
+ x.should == true
+ (x &&= false).should == false
+ x.should == false
+ end
+
+ pending "may not assign at all, depending on the truthiness of lhs" do
+ Object.new.instance_eval do
+ @falsey = false
+ @truthy = true
+ freeze
+ # lambda{ @truthy ||= 42 }.should_not raise_error
+ # lambda{ @falsey &&= 42 }.should_not raise_error
+ end
+ end
+
+ pending "uses short-circuit arg evaluation" do
+ x = 8
+ y = VariablesSpecs::OpAsgn.new
+ (x ||= y.do_side_effect).should == 8
+ y.side_effect.should == nil
+
+ x = nil
+ (x &&= y.do_side_effect).should == nil
+ y.side_effect.should == nil
+
+ y.a = 5
+ (x ||= y.do_side_effect).should == 5
+ y.side_effect.should == true
+ end
+end
+
+describe "Unconditional operator assignment 'obj.meth op= expr'" do
+ it "is equivalent to 'obj.meth = obj.meth op expr'" do
+ @x = VariablesSpecs::OpAsgn.new
+ @x.a = 13
+ (@x.a += 5).should == 18
+ @x.a.should == 18
+
+ @x.a = 17
+ (@x.a -= 11).should == 6
+ @x.a.should == 6
+
+ @x.a = 2
+ (@x.a *= 5).should == 10
+ @x.a.should == 10
+
+ @x.a = 36
+ (@x.a /= 9).should == 4
+ @x.a.should == 4
+
+ @x.a = 23
+ (@x.a %= 5).should == 3
+ @x.a.should == 3
+ (@x.a %= 3).should == 0
+ @x.a.should == 0
+
+ @x.a = 2
+ (@x.a **= 3).should == 8
+ @x.a.should == 8
+
+ @x.a = 4
+ (@x.a |= 3).should == 7
+ @x.a.should == 7
+ (@x.a |= 4).should == 7
+ @x.a.should == 7
+
+ @x.a = 6
+ (@x.a &= 3).should == 2
+ @x.a.should == 2
+ (@x.a &= 4).should == 0
+ @x.a.should == 0
+
+ # XOR
+ @x.a = 2
+ (@x.a ^= 3).should == 1
+ @x.a.should == 1
+ (@x.a ^= 4).should == 5
+ @x.a.should == 5
+
+ @x.a = 17
+ (@x.a <<= 3).should == 136
+ @x.a.should == 136
+
+ @x.a = 5
+ (@x.a >>= 1).should == 2
+ @x.a.should == 2
+ end
+end
+
+describe "Conditional operator assignment 'obj.meth op= expr'" do
+ it "is equivalent to 'obj.meth op obj.meth = expr'" do
+ @x = VariablesSpecs::OpAsgn.new
+ @x.a = nil
+ (@x.a ||= 17).should == 17
+ @x.a.should == 17
+ (@x.a ||= 2).should == 17
+ @x.a.should == 17
+
+ @x.a = false
+ (@x.a &&= true).should == false
+ @x.a.should == false
+ (@x.a &&= false).should == false
+ @x.a.should == false
+ @x.a = true
+ (@x.a &&= true).should == true
+ @x.a.should == true
+ (@x.a &&= false).should == false
+ @x.a.should == false
+ end
+
+ pending "may not assign at all, depending on the truthiness of lhs" do
+ m = mock("object")
+ m.should_receive(:foo).and_return(:truthy)
+ m.should_not_receive(:foo=)
+ # m.foo ||= 42
+
+ m.should_receive(:bar).and_return(false)
+ m.should_not_receive(:bar=)
+ # m.bar &&= 42
+ end
+
+ pending "uses short-circuit arg evaluation" do
+ x = 8
+ y = VariablesSpecs::OpAsgn.new
+ (x ||= y.do_side_effect).should == 8
+ y.side_effect.should == nil
+
+ x = nil
+ (x &&= y.do_side_effect).should == nil
+ y.side_effect.should == nil
+
+ y.a = 5
+ (x ||= y.do_side_effect).should == 5
+ y.side_effect.should == true
+ end
+end
+
+describe "Operator assignment 'obj.meth op= expr'" do
+ pending "evaluates lhs one time" do
+ x = VariablesSpecs::OpAsgn.new
+ x.a = 5
+ (x.do_more_side_effects.a += 5).should == 15
+ x.a.should == 15
+
+ x.a = 5
+ (x.do_more_side_effects.a -= 4).should == 6
+ x.a.should == 6
+
+ x.a = 2
+ (x.do_more_side_effects.a *= 5).should == 35
+ x.a.should == 35
+
+ x.a = 31
+ (x.do_more_side_effects.a /= 9).should == 4
+ x.a.should == 4
+
+ x.a = 18
+ (x.do_more_side_effects.a %= 5).should == 3
+ x.a.should == 3
+
+ x.a = 0
+ (x.do_more_side_effects.a **= 3).should == 125
+ x.a.should == 125
+
+ x.a = -1
+ (x.do_more_side_effects.a |= 3).should == 7
+ x.a.should == 7
+
+ x.a = 1
+ (x.do_more_side_effects.a &= 3).should == 2
+ x.a.should == 2
+
+ # XOR
+ x.a = -3
+ (x.do_more_side_effects.a ^= 3).should == 1
+ x.a.should == 1
+
+ x.a = 12
+ (x.do_more_side_effects.a <<= 3).should == 136
+ x.a.should == 136
+
+ x.a = 0
+ (x.do_more_side_effects.a >>= 1).should == 2
+ x.a.should == 2
+
+ x.a = nil
+ x.b = 0
+ # (x.do_bool_side_effects.a ||= 17).should == 17
+ # x.a.should == 17
+ # x.b.should == 1
+
+ x.a = false
+ x.b = 0
+ # # (x.do_bool_side_effects.a &&= true).should == false
+ # x.a.should == false
+ # x.b.should == 1
+ # (x.do_bool_side_effects.a &&= false).should == false
+ # x.a.should == false
+ # x.b.should == 2
+ # x.a = true
+ # x.b = 0
+ # (x.do_bool_side_effects.a &&= true).should == true
+ # x.a.should == true
+ # x.b.should == 1
+ # (x.do_bool_side_effects.a &&= false).should == false
+ # x.a.should == false
+ # x.b.should == 2
+ end
+end
+
+describe "Unconditional operator assignment 'obj[idx] op= expr'" do
+ pending "is equivalent to 'obj[idx] = obj[idx] op expr'" do
+ # x = [2,13,7]
+ # (x[1] += 5).should == 18
+ # x.should == [2,18,7]
+
+ # x = [17,6]
+ # (x[0] -= 11).should == 6
+ # x.should == [6,6]
+
+ # x = [nil,2,28]
+ # (x[2] *= 2).should == 56
+ # x.should == [nil,2,56]
+
+ # x = [3,9,36]
+ # (x[2] /= x[1]).should == 4
+ # x.should == [3,9,4]
+
+ # x = [23,4]
+ # (x[0] %= 5).should == 3
+ # x.should == [3,4]
+ # (x[0] %= 3).should == 0
+ # x.should == [0,4]
+
+ # x = [1,2,3]
+ # (x[1] **= 3).should == 8
+ # x.should == [1,8,3]
+
+ # x = [4,5,nil]
+ # (x[0] |= 3).should == 7
+ # x.should == [7,5,nil]
+ # (x[0] |= 4).should == 7
+ # x.should == [7,5,nil]
+
+ # x = [3,6,9]
+ # (x[1] &= 3).should == 2
+ # x.should == [3,2,9]
+ # (x[1] &= 4).should == 0
+ # x.should == [3,0,9]
+
+ # # XOR
+ # x = [0,1,2]
+ # (x[2] ^= 3).should == 1
+ # x.should == [0,1,1]
+ # (x[2] ^= 4).should == 5
+ # x.should == [0,1,5]
+
+ # x = [17]
+ # (x[0] <<= 3).should == 136
+ # x.should == [136]
+
+ # x = [nil,5,8]
+ # (x[1] >>= 1).should == 2
+ # x.should == [nil,2,8]
+ end
+end
+
+describe "Conditional operator assignment 'obj[idx] op= expr'" do
+ pending "is equivalent to 'obj[idx] op obj[idx] = expr'" do
+ # x = [1,nil,12]
+ # (x[1] ||= 17).should == 17
+ # x.should == [1,17,12]
+ # (x[1] ||= 2).should == 17
+ # x.should == [1,17,12]
+
+ # x = [true, false, false]
+ # (x[1] &&= true).should == false
+ # x.should == [true, false, false]
+ # (x[1] &&= false).should == false
+ # x.should == [true, false, false]
+ # (x[0] &&= true).should == true
+ # x.should == [true, false, false]
+ # (x[0] &&= false).should == false
+ # x.should == [false, false, false]
+ end
+
+ pending "may not assign at all, depending on the truthiness of lhs" do
+ # m = mock("object")
+ # m.should_receive(:[]).and_return(:truthy)
+ # m.should_not_receive(:[]=)
+ # m[:foo] ||= 42
+
+ # m = mock("object")
+ # m.should_receive(:[]).and_return(false)
+ # m.should_not_receive(:[]=)
+ # m[:bar] &&= 42
+ end
+
+ pending "uses short-circuit arg evaluation" do
+ # x = 8
+ # y = VariablesSpecs::OpAsgn.new
+ # (x ||= y.do_side_effect).should == 8
+ # y.side_effect.should == nil
+
+ # x = nil
+ # (x &&= y.do_side_effect).should == nil
+ # y.side_effect.should == nil
+
+ # y.a = 5
+ # (x ||= y.do_side_effect).should == 5
+ # y.side_effect.should == true
+ end
+end
+
+describe "Operator assignment 'obj[idx] op= expr'" do
+ class ArrayWithDefaultIndex < Array
+ def [](index=nil)
+ super(index || 0)
+ end
+
+ def []=(first_arg, second_arg=nil)
+ if second_arg
+ index = fist_arg
+ value = second_arg
+ else
+ index = 0
+ value = first_arg
+ end
+
+ super(index, value)
+ end
+ end
+
+ pending "handles empty index (idx) arguments" do
+# array = ArrayWithDefaultIndex.new
+# array << 1
+# (array[] += 5).should == 6
+ end
+
+ pending "handles complex index (idx) arguments" do
+# x = [1,2,3,4]
+# (x[0,2] += [5]).should == [1,2,5]
+# x.should == [1,2,5,3,4]
+# (x[0,2] += [3,4]).should == [1,2,3,4]
+# x.should == [1,2,3,4,5,3,4]
+
+# (x[2..3] += [8]).should == [3,4,8]
+# x.should == [1,2,3,4,8,5,3,4]
+
+# y = VariablesSpecs::OpAsgn.new
+# y.a = 1
+# (x[y.do_side_effect] *= 2).should == 4
+# x.should == [1,4,3,4,8,5,3,4]
+
+# h = {'key1' => 23, 'key2' => 'val'}
+# (h['key1'] %= 5).should == 3
+# (h['key2'] += 'ue').should == 'value'
+# h.should == {'key1' => 3, 'key2' => 'value'}
+ end
+
+ pending "handles empty splat index (idx) arguments" do
+# array = ArrayWithDefaultIndex.new
+# array << 5
+# splat_index = []
+
+# (array[*splat_index] += 5).should == 10
+# array.should== [10]
+ end
+
+ pending "handles single splat index (idx) arguments" do
+# array = [1,2,3,4]
+# splat_index = [0]
+
+# (array[*splat_index] += 5).should == 6
+# array.should == [6,2,3,4]
+ end
+
+ pending "handles multiple splat index (idx) arguments" do
+# array = [1,2,3,4]
+# splat_index = [0,2]
+
+# (array[*splat_index] += [5]).should == [1,2,5]
+# array.should == [1,2,5,3,4]
+ end
+
+ pending "handles splat index (idx) arguments with normal arguments" do
+# array = [1,2,3,4]
+# splat_index = [2]
+
+# (array[0, *splat_index] += [5]).should == [1,2,5]
+# array.should == [1,2,5,3,4]
+ end
+
+ # This example fails on 1.9 because of bug #2050
+ pending "returns result of rhs not result of []=" do
+# a = VariablesSpecs::Hashalike.new
+
+# (a[123] = 2).should == 2
+# (a[123] += 2).should == 125
+# (a[123] -= 2).should == 121
+# (a[123] *= 2).should == 246
+# # Guard against the Mathn library
+# # TODO: Make these specs not rely on specific behaviour / result values
+# # by using mocks.
+# conflicts_with :Prime do
+# (a[123] /= 2).should == 61
+# end
+# (a[123] %= 2).should == 1
+# (a[123] **= 2).should == 15129
+# (a[123] |= 2).should == 123
+# (a[123] &= 2).should == 2
+# (a[123] ^= 2).should == 121
+# (a[123] <<= 2).should == 492
+# (a[123] >>= 2).should == 30
+# (a[123] ||= 2).should == 123
+# (a[nil] ||= 2).should == 2
+# (a[123] &&= 2).should == 2
+# (a[nil] &&= 2).should == nil
+ end
+end
+
describe "Single assignment" do
it "Assignment does not modify the lhs, it reassigns its reference" do
a = 'Foobar'
b = a
b = 'Bazquux'
@@ -145,11 +1106,11 @@
b << 1
a.should == [1]
end
it "If rhs has multiple arguments, lhs becomes an Array of them" do
- a = [1, 2, 3]
+ a = 1, 2, 3
a.should == [1, 2, 3]
a = 1, (), 3
a.should == [1, nil, 3]
end
@@ -174,6 +1135,241 @@
it "If rhs has too many arguments, the extra ones are silently not assigned anywhere" do
a, b = 1, 2, 3
a.should == 1
b.should == 2
end
-end
\ No newline at end of file
+
+ it "The assignments are done in parallel so that lhs and rhs are independent of eachother without copying" do
+ o_of_a, o_of_b = mock('a'), mock('b')
+ a, b = o_of_a, o_of_b
+ a, b = b, a
+ a.should equal(o_of_b)
+ b.should equal(o_of_a)
+ end
+end
+
+describe "Multiple assignments with splats" do
+ ruby_version_is ""..."1.9" do
+ it "* on the lhs has to be applied to the last parameter" do
+ lambda { eval 'a, *b, c = 1, 2, 3' }.should raise_error(SyntaxError)
+ end
+ end
+
+ it "* on the lhs collects all parameters from its position onwards as an Array or an empty Array" do
+ a, *b = 1, 2
+ c, *d = 1
+ e, *f = 1, 2, 3
+ g, *h = 1, [2, 3]
+ *i = 1, [2,3]
+ *k = 1,2,3
+
+ a.should == 1
+ b.should == [2]
+ c.should == 1
+ d.should == []
+ e.should == 1
+ f.should == [2, 3]
+ g.should == 1
+ h.should == [[2, 3]]
+ i.should == [1, [2, 3]]
+ k.should == [1,2,3]
+ end
+
+ ruby_version_is ""..."1.9" do
+ it "* on the LHS returns the Array on the RHS enclosed in an Array" do
+ *j = [1,2,3]
+ j.should == [[1,2,3]]
+ end
+ end
+
+ ruby_version_is "1.9" do
+ it "* on the LHS returns the Array on the RHS without enclosing it in an Array" do
+ *j = [1,2,3]
+ j.should == [1,2,3]
+ end
+ end
+end
+
+describe "Multiple assignments with grouping" do
+ pending "A group on the lhs is considered one position and treats its corresponding rhs position like an Array" do
+ # a, (b, c), d = 1, 2, 3, 4
+ # e, (f, g), h = 1, [2, 3, 4], 5
+ # i, (j, k), l = 1, 2, 3
+ a.should == 1
+ b.should == 2
+ c.should == nil
+ d.should == 3
+ e.should == 1
+ f.should == 2
+ g.should == 3
+ h.should == 5
+ i.should == 1
+ j.should == 2
+ k.should == nil
+ l.should == 3
+ end
+
+ pending "supports multiple levels of nested groupings" do
+ # a,(b,(c,d)) = 1,[2,[3,4]]
+ a.should == 1
+ b.should == 2
+ c.should == 3
+ d.should == 4
+
+ # a,(b,(c,d)) = [1,[2,[3,4]]]
+ a.should == 1
+ b.should == 2
+ c.should == 3
+ d.should == 4
+
+ x = [1,[2,[3,4]]]
+ # a,(b,(c,d)) = x
+ a.should == 1
+ b.should == 2
+ c.should == 3
+ d.should == 4
+ end
+
+ pending "rhs cannot use parameter grouping, it is a syntax error" do
+ lambda { eval '(a, b) = (1, 2)' }.should raise_error(SyntaxError)
+ end
+end
+
+# TODO: merge the following two describe blocks and partition the specs
+# into distinct cases.
+describe "Multiple assignment" do
+ pending do
+ not_compliant_on :rubinius do
+ it "has the proper return value" do
+ # (a,b,*c = *[5,6,7,8,9,10]).should == [5,6,7,8,9,10]
+ # (d,e = VariablesSpecs.reverse_foo(4,3)).should == [3,4]
+ # (f,g,h = VariablesSpecs.reverse_foo(6,7)).should == [7,6]
+ # (i,*j = *[5,6,7]).should == [5,6,7]
+ # (k,*l = [5,6,7]).should == [5,6,7]
+ a.should == 5
+ b.should == 6
+ c.should == [7,8,9,10]
+ d.should == 3
+ e.should == 4
+ f.should == 7
+ g.should == 6
+ h.should == nil
+ i.should == 5
+ j.should == [6,7]
+ k.should == 5
+ l.should == [6,7]
+ end
+ end
+ end
+
+ # TODO: write Rubinius versions
+end
+
+# For now, masgn is deliberately non-compliant with MRI wrt the return val from an masgn.
+# Rubinius returns true as the result of the assignment, but MRI returns an array
+# containing all the elements on the rhs. As this result is never used, the cost
+# of creating and then discarding this array is avoided
+describe "Multiple assignment, array-style" do
+ pending do
+ not_compliant_on :rubinius do
+ it "returns an array of all rhs values" do
+ (a,b = 5,6,7).should == [5,6,7]
+ a.should == 5
+ b.should == 6
+
+ (c,d,*e = 99,8).should == [99,8]
+ c.should == 99
+ d.should == 8
+ e.should == []
+
+ (f,g,h = 99,8).should == [99,8]
+ f.should == 99
+ g.should == 8
+ h.should == nil
+ end
+ end
+
+ deviates_on :rubinius do
+ it "returns true" do
+ (a,b = 5,6,7).should == true
+ a.should == 5
+ b.should == 6
+
+ (c,d,*e = 99,8).should == true
+ c.should == 99
+ d.should == 8
+ e.should == []
+
+ (f,g,h = 99,8).should == true
+ f.should == 99
+ g.should == 8
+ h.should == nil
+ end
+ end
+ end
+end
+
+describe "Scope of variables" do
+ pending "instance variables not overwritten by local variable in each block" do
+
+ class ScopeVariables
+ attr_accessor :v
+
+ def initialize
+ @v = ['a', 'b', 'c']
+ end
+
+ def check_access
+ v.should == ['a', 'b', 'c']
+ self.v.should == ['a', 'b', 'c']
+ end
+
+ def check_local_variable
+ v = nil
+ self.v.should == ['a', 'b', 'c']
+ end
+
+ def check_each_block
+ self.v.each { |v|
+ # Don't actually do anything
+ }
+ self.v.should == ['a', 'b', 'c']
+ v.should == ['a', 'b', 'c']
+ self.v.object_id.should == v.object_id
+ end
+ end # Class ScopeVariables
+
+ instance = ScopeVariables.new()
+ instance.check_access
+ instance.check_local_variable
+ instance.check_each_block
+ end
+end
+
+describe "A local variable in a #define_method scope" do
+ pending do
+ ruby_bug '#1322', '1.8.7.228' do
+ it "shares the lexical scope containing the call to #define_method" do
+ # We need a new scope to reproduce this bug.
+ handle = mock("handle for containing scope method")
+
+ def handle.produce_bug
+ local = 1
+
+ klass = Class.new
+ klass.send :define_method, :set_local do |arg|
+ lambda { local = 2 }.call
+ end
+
+ # We must call with at least one argument to reproduce the bug.
+ klass.new.set_local(nil)
+
+ local
+ end
+
+ handle.produce_bug.should == 2
+ end
+ end
+ end
+end
+
+# # language_version __FILE__, "variables"