mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Move spec/rubyspec to spec/ruby for consistency
* Other ruby implementations use the spec/ruby directory. [Misc #13792] [ruby-core:82287] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59979 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
75bfc6440d
commit
1d15d5f080
4370 changed files with 0 additions and 0 deletions
32
spec/ruby/language/BEGIN_spec.rb
Normal file
32
spec/ruby/language/BEGIN_spec.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "The BEGIN keyword" do
|
||||
before :each do
|
||||
ScratchPad.record []
|
||||
end
|
||||
|
||||
it "runs in a shared scope" do
|
||||
eval("BEGIN { var_in_begin = 'foo' }; var_in_begin").should == "foo"
|
||||
end
|
||||
|
||||
it "accesses variables outside the eval scope" do
|
||||
outside_var = 'foo'
|
||||
eval("BEGIN { var_in_begin = outside_var }; var_in_begin").should == "foo"
|
||||
end
|
||||
|
||||
it "must appear in a top-level context" do
|
||||
lambda { eval "1.times { BEGIN { 1 } }" }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "runs first in a given code unit" do
|
||||
eval "ScratchPad << 'foo'; BEGIN { ScratchPad << 'bar' }"
|
||||
|
||||
ScratchPad.recorded.should == ['bar', 'foo']
|
||||
end
|
||||
|
||||
it "runs multiple begins in FIFO order" do
|
||||
eval "BEGIN { ScratchPad << 'foo' }; BEGIN { ScratchPad << 'bar' }"
|
||||
|
||||
ScratchPad.recorded.should == ['foo', 'bar']
|
||||
end
|
||||
end
|
30
spec/ruby/language/README
Normal file
30
spec/ruby/language/README
Normal file
|
@ -0,0 +1,30 @@
|
|||
There are numerous possible way of categorizing the entities and concepts that
|
||||
make up a programming language. Ruby has a fairly large number of reserved
|
||||
words. These words significantly describe major elements of the language,
|
||||
including flow control constructs like 'for' and 'while', conditional
|
||||
execution like 'if' and 'unless', exceptional execution control like 'rescue',
|
||||
etc. There are also literals for the basic "types" like String, Regexp, Array
|
||||
and Fixnum.
|
||||
|
||||
Behavorial specifications describe the behavior of concrete entities. Rather
|
||||
than using concepts of computation to organize these spec files, we use
|
||||
entities of the Ruby language. Consider looking at any syntactic element of a
|
||||
Ruby program. With (almost) no ambiguity, one can identify it as a literal,
|
||||
reserved word, variable, etc. There is a spec file that corresponds to each
|
||||
literal construct and most reserved words, with the exceptions noted below.
|
||||
There are also several files that are more difficult to classify: all
|
||||
predefined variables, constants, and objects (predefined_spec.rb), the
|
||||
precedence of all operators (precedence_spec.rb), the behavior of assignment
|
||||
to variables (variables_spec.rb), the behavior of subprocess execution
|
||||
(execution_spec.rb), the behavior of the raise method as it impacts the
|
||||
execution of a Ruby program (raise_spec.rb), and the block entities like
|
||||
'begin', 'do', ' { ... }' (block_spec.rb).
|
||||
|
||||
Several reserved words and other entities are combined with the primary
|
||||
reserved word or entity to which they are related:
|
||||
|
||||
false, true, nil, self predefined_spec.rb
|
||||
in for_spec.rb
|
||||
then, elsif if_spec.rb
|
||||
when case_spec.rb
|
||||
catch throw_spec.rb
|
246
spec/ruby/language/alias_spec.rb
Normal file
246
spec/ruby/language/alias_spec.rb
Normal file
|
@ -0,0 +1,246 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
class AliasObject
|
||||
attr :foo
|
||||
attr_reader :bar
|
||||
attr_accessor :baz
|
||||
|
||||
def prep; @foo = 3; @bar = 4; end
|
||||
def value; 5; end
|
||||
def false_value; 6; end
|
||||
def self.klass_method; 7; end
|
||||
end
|
||||
|
||||
describe "The alias keyword" do
|
||||
before :each do
|
||||
@obj = AliasObject.new
|
||||
@meta = class << @obj;self;end
|
||||
end
|
||||
|
||||
it "creates a new name for an existing method" do
|
||||
@meta.class_eval do
|
||||
alias __value value
|
||||
end
|
||||
@obj.__value.should == 5
|
||||
end
|
||||
|
||||
it "works with a simple symbol on the left-hand side" do
|
||||
@meta.class_eval do
|
||||
alias :a value
|
||||
end
|
||||
@obj.a.should == 5
|
||||
end
|
||||
|
||||
it "works with a single quoted symbol on the left-hand side" do
|
||||
@meta.class_eval do
|
||||
alias :'a' value
|
||||
end
|
||||
@obj.a.should == 5
|
||||
end
|
||||
|
||||
it "works with a doubule quoted symbol on the left-hand side" do
|
||||
@meta.class_eval do
|
||||
alias :"a" value
|
||||
end
|
||||
@obj.a.should == 5
|
||||
end
|
||||
|
||||
it "works with an interoplated symbol on the left-hand side" do
|
||||
@meta.class_eval do
|
||||
alias :"#{'a'}" value
|
||||
end
|
||||
@obj.a.should == 5
|
||||
end
|
||||
|
||||
it "works with a simple symbol on the right-hand side" do
|
||||
@meta.class_eval do
|
||||
alias a :value
|
||||
end
|
||||
@obj.a.should == 5
|
||||
end
|
||||
|
||||
it "works with a single quoted symbol on the right-hand side" do
|
||||
@meta.class_eval do
|
||||
alias a :'value'
|
||||
end
|
||||
@obj.a.should == 5
|
||||
end
|
||||
|
||||
it "works with a doubule quoted symbol on the right-hand side" do
|
||||
@meta.class_eval do
|
||||
alias a :"value"
|
||||
end
|
||||
@obj.a.should == 5
|
||||
end
|
||||
|
||||
it "works with an interoplated symbol on the right-hand side" do
|
||||
@meta.class_eval do
|
||||
alias a :"#{'value'}"
|
||||
end
|
||||
@obj.a.should == 5
|
||||
end
|
||||
|
||||
it "adds the new method to the list of methods" do
|
||||
original_methods = @obj.methods
|
||||
@meta.class_eval do
|
||||
alias __value value
|
||||
end
|
||||
(@obj.methods - original_methods).map {|m| m.to_s }.should == ["__value"]
|
||||
end
|
||||
|
||||
it "adds the new method to the list of public methods" do
|
||||
original_methods = @obj.public_methods
|
||||
@meta.class_eval do
|
||||
alias __value value
|
||||
end
|
||||
(@obj.public_methods - original_methods).map {|m| m.to_s }.should == ["__value"]
|
||||
end
|
||||
|
||||
it "overwrites an existing method with the target name" do
|
||||
@meta.class_eval do
|
||||
alias false_value value
|
||||
end
|
||||
@obj.false_value.should == 5
|
||||
end
|
||||
|
||||
it "is reversible" do
|
||||
@meta.class_eval do
|
||||
alias __value value
|
||||
alias value false_value
|
||||
end
|
||||
@obj.value.should == 6
|
||||
|
||||
@meta.class_eval do
|
||||
alias value __value
|
||||
end
|
||||
@obj.value.should == 5
|
||||
end
|
||||
|
||||
it "operates on the object's metaclass when used in instance_eval" do
|
||||
@obj.instance_eval do
|
||||
alias __value value
|
||||
end
|
||||
|
||||
@obj.__value.should == 5
|
||||
lambda { AliasObject.new.__value }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "operates on the class/module metaclass when used in instance_eval" do
|
||||
AliasObject.instance_eval do
|
||||
alias __klass_method klass_method
|
||||
end
|
||||
|
||||
AliasObject.__klass_method.should == 7
|
||||
lambda { Object.__klass_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "operates on the class/module metaclass when used in instance_exec" do
|
||||
AliasObject.instance_exec do
|
||||
alias __klass_method2 klass_method
|
||||
end
|
||||
|
||||
AliasObject.__klass_method2.should == 7
|
||||
lambda { Object.__klass_method2 }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "operates on methods defined via attr, attr_reader, and attr_accessor" do
|
||||
@obj.prep
|
||||
@obj.instance_eval do
|
||||
alias afoo foo
|
||||
alias abar bar
|
||||
alias abaz baz
|
||||
end
|
||||
|
||||
@obj.afoo.should == 3
|
||||
@obj.abar.should == 4
|
||||
@obj.baz = 5
|
||||
@obj.abaz.should == 5
|
||||
end
|
||||
|
||||
it "operates on methods with splat arguments" do
|
||||
class AliasObject2;end
|
||||
AliasObject2.class_eval do
|
||||
def test(*args)
|
||||
4
|
||||
end
|
||||
def test_with_check(*args)
|
||||
test_without_check(*args)
|
||||
end
|
||||
alias test_without_check test
|
||||
alias test test_with_check
|
||||
end
|
||||
AliasObject2.new.test(1,2,3,4,5).should == 4
|
||||
end
|
||||
|
||||
it "operates on methods with splat arguments on eigenclasses" do
|
||||
@meta.class_eval do
|
||||
def test(*args)
|
||||
4
|
||||
end
|
||||
def test_with_check(*args)
|
||||
test_without_check(*args)
|
||||
end
|
||||
alias test_without_check test
|
||||
alias test test_with_check
|
||||
end
|
||||
@obj.test(1,2,3,4,5).should == 4
|
||||
end
|
||||
|
||||
it "operates on methods with splat arguments defined in a superclass" do
|
||||
alias_class = Class.new
|
||||
alias_class.class_eval do
|
||||
def test(*args)
|
||||
4
|
||||
end
|
||||
def test_with_check(*args)
|
||||
test_without_check(*args)
|
||||
end
|
||||
end
|
||||
sub = Class.new(alias_class) do
|
||||
alias test_without_check test
|
||||
alias test test_with_check
|
||||
end
|
||||
sub.new.test(1,2,3,4,5).should == 4
|
||||
end
|
||||
|
||||
it "operates on methods with splat arguments defined in a superclass using text block for class eval" do
|
||||
class Sub < AliasObject;end
|
||||
AliasObject.class_eval <<-code
|
||||
def test(*args)
|
||||
4
|
||||
end
|
||||
def test_with_check(*args)
|
||||
test_without_check(*args)
|
||||
end
|
||||
alias test_without_check test
|
||||
alias test test_with_check
|
||||
code
|
||||
Sub.new.test("testing").should == 4
|
||||
end
|
||||
|
||||
it "is not allowed against Fixnum or String instances" do
|
||||
lambda do
|
||||
1.instance_eval do
|
||||
alias :foo :to_s
|
||||
end
|
||||
end.should raise_error(TypeError)
|
||||
|
||||
lambda do
|
||||
:blah.instance_eval do
|
||||
alias :foo :to_s
|
||||
end
|
||||
end.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "on top level defines the alias on Object" do
|
||||
# because it defines on the default definee / current module
|
||||
ruby_exe("def foo; end; alias bla foo; print method(:bla).owner", escape: true).should == "Object"
|
||||
end
|
||||
|
||||
it "raises a NameError when passed a missing name" do
|
||||
lambda { @meta.class_eval { alias undef_method not_exist } }.should raise_error(NameError) { |e|
|
||||
# a NameError and not a NoMethodError
|
||||
e.class.should == NameError
|
||||
}
|
||||
end
|
||||
end
|
80
spec/ruby/language/and_spec.rb
Normal file
80
spec/ruby/language/and_spec.rb
Normal file
|
@ -0,0 +1,80 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "The '&&' statement" do
|
||||
|
||||
it "short-circuits evaluation at the first condition to be false" do
|
||||
x = nil
|
||||
true && false && x = 1
|
||||
x.should be_nil
|
||||
end
|
||||
|
||||
it "evaluates to the first condition not to be true" do
|
||||
value = nil
|
||||
(value && nil).should == nil
|
||||
(value && false).should == nil
|
||||
value = false
|
||||
(value && nil).should == false
|
||||
(value && false).should == false
|
||||
|
||||
("yes" && 1 && nil && true).should == nil
|
||||
("yes" && 1 && false && true).should == false
|
||||
end
|
||||
|
||||
it "evaluates to the last condition if all are true" do
|
||||
("yes" && 1).should == 1
|
||||
(1 && "yes").should == "yes"
|
||||
end
|
||||
|
||||
it "evaluates the full set of chained conditions during assignment" do
|
||||
x, y = nil
|
||||
x = 1 && y = 2
|
||||
# "1 && y = 2" is evaluated and then assigned to x
|
||||
x.should == 2
|
||||
end
|
||||
|
||||
it "treats empty expressions as nil" do
|
||||
(() && true).should be_nil
|
||||
(true && ()).should be_nil
|
||||
(() && ()).should be_nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "The 'and' statement" do
|
||||
it "short-circuits evaluation at the first condition to be false" do
|
||||
x = nil
|
||||
true and false and x = 1
|
||||
x.should be_nil
|
||||
end
|
||||
|
||||
it "evaluates to the first condition not to be true" do
|
||||
value = nil
|
||||
(value and nil).should == nil
|
||||
(value and false).should == nil
|
||||
value = false
|
||||
(value and nil).should == false
|
||||
(value and false).should == false
|
||||
|
||||
("yes" and 1 and nil and true).should == nil
|
||||
("yes" and 1 and false and true).should == false
|
||||
end
|
||||
|
||||
it "evaluates to the last condition if all are true" do
|
||||
("yes" and 1).should == 1
|
||||
(1 and "yes").should == "yes"
|
||||
end
|
||||
|
||||
it "when used in assignment, evaluates and assigns expressions individually" do
|
||||
x, y = nil
|
||||
x = 1 and y = 2
|
||||
# evaluates (x=1) and (y=2)
|
||||
x.should == 1
|
||||
end
|
||||
|
||||
it "treats empty expressions as nil" do
|
||||
(() and true).should be_nil
|
||||
(true and ()).should be_nil
|
||||
(() and ()).should be_nil
|
||||
end
|
||||
|
||||
end
|
155
spec/ruby/language/array_spec.rb
Normal file
155
spec/ruby/language/array_spec.rb
Normal file
|
@ -0,0 +1,155 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/array', __FILE__)
|
||||
|
||||
describe "Array literals" do
|
||||
it "[] should return a new array populated with the given elements" do
|
||||
array = [1, 'a', nil]
|
||||
array.should be_kind_of(Array)
|
||||
array[0].should == 1
|
||||
array[1].should == 'a'
|
||||
array[2].should == nil
|
||||
end
|
||||
|
||||
it "[] treats empty expressions as nil elements" do
|
||||
array = [0, (), 2, (), 4]
|
||||
array.should be_kind_of(Array)
|
||||
array[0].should == 0
|
||||
array[1].should == nil
|
||||
array[2].should == 2
|
||||
array[3].should == nil
|
||||
array[4].should == 4
|
||||
end
|
||||
|
||||
it "[] accepts a literal hash without curly braces as its only parameter" do
|
||||
["foo" => :bar, baz: 42].should == [{"foo" => :bar, baz: 42}]
|
||||
end
|
||||
|
||||
it "[] accepts a literal hash without curly braces as its last parameter" do
|
||||
["foo", "bar" => :baz].should == ["foo", {"bar" => :baz}]
|
||||
[1, 2, 3 => 6, 4 => 24].should == [1, 2, {3 => 6, 4 => 24}]
|
||||
end
|
||||
|
||||
it "[] treats splatted nil as no element" do
|
||||
[*nil].should == []
|
||||
[1, *nil].should == [1]
|
||||
[1, 2, *nil].should == [1, 2]
|
||||
[1, *nil, 3].should == [1, 3]
|
||||
[*nil, *nil, *nil].should == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "Bareword array literal" do
|
||||
it "%w() transforms unquoted barewords into an array" do
|
||||
a = 3
|
||||
%w(a #{3+a} 3).should == ["a", '#{3+a}', "3"]
|
||||
end
|
||||
|
||||
it "%W() transforms unquoted barewords into an array, supporting interpolation" do
|
||||
a = 3
|
||||
%W(a #{3+a} 3).should == ["a", '6', "3"]
|
||||
end
|
||||
|
||||
it "%W() always treats interpolated expressions as a single word" do
|
||||
a = "hello world"
|
||||
%W(a b c #{a} d e).should == ["a", "b", "c", "hello world", "d", "e"]
|
||||
end
|
||||
|
||||
it "treats consecutive whitespace characters the same as one" do
|
||||
%w(a b c d).should == ["a", "b", "c", "d"]
|
||||
%W(hello
|
||||
world).should == ["hello", "world"]
|
||||
end
|
||||
|
||||
it "treats whitespace as literals characters when escaped by a backslash" do
|
||||
%w(a b\ c d e).should == ["a", "b c", "d", "e"]
|
||||
%w(a b\
|
||||
c d).should == ["a", "b\nc", "d"]
|
||||
%W(a\ b\tc).should == ["a ", "b\tc"]
|
||||
%W(white\ \ \ \ \ space).should == ["white ", " ", " ", " space"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "The unpacking splat operator (*)" do
|
||||
it "when applied to a literal nested array, unpacks its elements into the containing array" do
|
||||
[1, 2, *[3, 4, 5]].should == [1, 2, 3, 4, 5]
|
||||
end
|
||||
|
||||
it "when applied to a nested referenced array, unpacks its elements into the containing array" do
|
||||
splatted_array = [3, 4, 5]
|
||||
[1, 2, *splatted_array].should == [1, 2, 3, 4, 5]
|
||||
end
|
||||
|
||||
it "returns a new array containing the same values when applied to an array inside an empty array" do
|
||||
splatted_array = [3, 4, 5]
|
||||
[*splatted_array].should == splatted_array
|
||||
[*splatted_array].should_not equal(splatted_array)
|
||||
end
|
||||
|
||||
it "unpacks the start and count arguments in an array slice assignment" do
|
||||
alphabet_1 = ['a'..'z'].to_a
|
||||
alphabet_2 = alphabet_1.dup
|
||||
start_and_count_args = [1, 10]
|
||||
|
||||
alphabet_1[1, 10] = 'a'
|
||||
alphabet_2[*start_and_count_args] = 'a'
|
||||
|
||||
alphabet_1.should == alphabet_2
|
||||
end
|
||||
|
||||
it "unpacks arguments as if they were listed statically" do
|
||||
static = [1,2,3,4]
|
||||
receiver = static.dup
|
||||
args = [0,1]
|
||||
static[0,1] = []
|
||||
static.should == [2,3,4]
|
||||
receiver[*args] = []
|
||||
receiver.should == static
|
||||
end
|
||||
|
||||
it "unpacks a literal array into arguments in a method call" do
|
||||
tester = ArraySpec::Splat.new
|
||||
tester.unpack_3args(*[1, 2, 3]).should == [1, 2, 3]
|
||||
tester.unpack_4args(1, 2, *[3, 4]).should == [1, 2, 3, 4]
|
||||
tester.unpack_4args("a", %w(b c), *%w(d e)).should == ["a", ["b", "c"], "d", "e"]
|
||||
end
|
||||
|
||||
it "unpacks a referenced array into arguments in a method call" do
|
||||
args = [1, 2, 3]
|
||||
tester = ArraySpec::Splat.new
|
||||
tester.unpack_3args(*args).should == [1, 2, 3]
|
||||
tester.unpack_4args(0, *args).should == [0, 1, 2, 3]
|
||||
end
|
||||
|
||||
it "when applied to a non-Array value attempts to coerce it to Array if the object respond_to?(:to_a)" do
|
||||
obj = mock("pseudo-array")
|
||||
obj.should_receive(:to_a).and_return([2, 3, 4])
|
||||
[1, *obj].should == [1, 2, 3, 4]
|
||||
end
|
||||
|
||||
it "when applied to a non-Array value uses it unchanged if it does not respond_to?(:to_a)" do
|
||||
obj = Object.new
|
||||
obj.should_not respond_to(:to_a)
|
||||
[1, *obj].should == [1, obj]
|
||||
end
|
||||
|
||||
it "when applied to a BasicObject coerces it to Array if it respond_to?(:to_a)" do
|
||||
obj = BasicObject.new
|
||||
def obj.to_a; [2, 3, 4]; end
|
||||
[1, *obj].should == [1, 2, 3, 4]
|
||||
end
|
||||
|
||||
it "can be used before other non-splat elements" do
|
||||
a = [1, 2]
|
||||
[0, *a, 3].should == [0, 1, 2, 3]
|
||||
end
|
||||
|
||||
it "can be used multiple times in the same containing array" do
|
||||
a = [1, 2]
|
||||
b = [1, 0]
|
||||
[*a, 3, *a, *b].should == [1, 2, 3, 1, 2, 1, 0]
|
||||
end
|
||||
end
|
||||
|
||||
describe "The packing splat operator (*)" do
|
||||
|
||||
end
|
865
spec/ruby/language/block_spec.rb
Normal file
865
spec/ruby/language/block_spec.rb
Normal file
|
@ -0,0 +1,865 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/block', __FILE__)
|
||||
|
||||
describe "A block yielded a single" do
|
||||
before :all do
|
||||
def m(a) yield a end
|
||||
end
|
||||
|
||||
context "Array" do
|
||||
it "assigns the Array to a single argument" do
|
||||
m([1, 2]) { |a| a }.should == [1, 2]
|
||||
end
|
||||
|
||||
it "receives the identical Array object" do
|
||||
ary = [1, 2]
|
||||
m(ary) { |a| a }.should equal(ary)
|
||||
end
|
||||
|
||||
it "assigns the Array to a single rest argument" do
|
||||
m([1, 2, 3]) { |*a| a }.should == [[1, 2, 3]]
|
||||
end
|
||||
|
||||
it "assigns the first element to a single argument with trailing comma" do
|
||||
m([1, 2]) { |a, | a }.should == 1
|
||||
end
|
||||
|
||||
it "assigns elements to required arguments" do
|
||||
m([1, 2, 3]) { |a, b| [a, b] }.should == [1, 2]
|
||||
end
|
||||
|
||||
it "assigns nil to unassigned required arguments" do
|
||||
m([1, 2]) { |a, *b, c, d| [a, b, c, d] }.should == [1, [], 2, nil]
|
||||
end
|
||||
|
||||
it "assigns elements to optional arguments" do
|
||||
m([1, 2]) { |a=5, b=4, c=3| [a, b, c] }.should == [1, 2, 3]
|
||||
end
|
||||
|
||||
it "assgins elements to post arguments" do
|
||||
m([1, 2]) { |a=5, b, c, d| [a, b, c, d] }.should == [5, 1, 2, nil]
|
||||
end
|
||||
|
||||
it "assigns elements to required arguments when a keyword rest argument is present" do
|
||||
m([1, 2]) { |a, **k| [a, k] }.should == [1, {}]
|
||||
end
|
||||
|
||||
it "assigns elements to mixed argument types" do
|
||||
result = m([1, 2, 3, {x: 9}]) { |a, b=5, *c, d, e: 2, **k| [a, b, c, d, e, k] }
|
||||
result.should == [1, 2, [], 3, 2, {x: 9}]
|
||||
end
|
||||
|
||||
it "assigns symbol keys from a Hash to keyword arguments" do
|
||||
result = m(["a" => 1, a: 10]) { |a=nil, **b| [a, b] }
|
||||
result.should == [{"a" => 1}, a: 10]
|
||||
end
|
||||
|
||||
it "assigns symbol keys from a Hash returned by #to_hash to keyword arguments" do
|
||||
obj = mock("coerce block keyword arguments")
|
||||
obj.should_receive(:to_hash).and_return({"a" => 1, b: 2})
|
||||
|
||||
result = m([obj]) { |a=nil, **b| [a, b] }
|
||||
result.should == [{"a" => 1}, b: 2]
|
||||
end
|
||||
|
||||
ruby_version_is "2.2.1" do # SEGV on MRI 2.2.0
|
||||
it "calls #to_hash on the argument but does not use the result when no keywords are present" do
|
||||
obj = mock("coerce block keyword arguments")
|
||||
obj.should_receive(:to_hash).and_return({"a" => 1, "b" => 2})
|
||||
|
||||
result = m([obj]) { |a=nil, **b| [a, b] }
|
||||
result.should == [{"a" => 1, "b" => 2}, {}]
|
||||
end
|
||||
end
|
||||
|
||||
it "assigns non-symbol keys to non-keyword arguments" do
|
||||
result = m(["a" => 10, b: 2]) { |a=nil, **b| [a, b] }
|
||||
result.should == [{"a" => 10}, {b: 2}]
|
||||
end
|
||||
|
||||
it "does not treat hashes with string keys as keyword arguments" do
|
||||
result = m(["a" => 10]) { |a = nil, **b| [a, b] }
|
||||
result.should == [{"a" => 10}, {}]
|
||||
end
|
||||
|
||||
it "calls #to_hash on the last element if keyword arguments are present" do
|
||||
obj = mock("destructure block keyword arguments")
|
||||
obj.should_receive(:to_hash).and_return({x: 9})
|
||||
|
||||
result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] }
|
||||
result.should == [1, [2], 3, {x: 9}]
|
||||
end
|
||||
|
||||
it "assigns the last element to a non-keyword argument if #to_hash returns nil" do
|
||||
obj = mock("destructure block keyword arguments")
|
||||
obj.should_receive(:to_hash).and_return(nil)
|
||||
|
||||
result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] }
|
||||
result.should == [1, [2, 3], obj, {}]
|
||||
end
|
||||
|
||||
it "calls #to_hash on the last element when there are more arguments than parameters" do
|
||||
x = mock("destructure matching block keyword argument")
|
||||
x.should_receive(:to_hash).and_return({x: 9})
|
||||
|
||||
result = m([1, 2, 3, {y: 9}, 4, 5, x]) { |a, b=5, c, **k| [a, b, c, k] }
|
||||
result.should == [1, 2, 3, {x: 9}]
|
||||
end
|
||||
|
||||
it "raises a TypeError if #to_hash does not return a Hash" do
|
||||
obj = mock("destructure block keyword arguments")
|
||||
obj.should_receive(:to_hash).and_return(1)
|
||||
|
||||
lambda { m([1, 2, 3, obj]) { |a, *b, c, **k| } }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises the error raised inside #to_hash" do
|
||||
obj = mock("destructure block keyword arguments")
|
||||
error = RuntimeError.new("error while converting to a hash")
|
||||
obj.should_receive(:to_hash).and_raise(error)
|
||||
|
||||
lambda { m([1, 2, 3, obj]) { |a, *b, c, **k| } }.should raise_error(error)
|
||||
end
|
||||
|
||||
it "does not call #to_ary on the Array" do
|
||||
ary = [1, 2]
|
||||
ary.should_not_receive(:to_ary)
|
||||
|
||||
m(ary) { |a, b, c| [a, b, c] }.should == [1, 2, nil]
|
||||
end
|
||||
end
|
||||
|
||||
context "Object" do
|
||||
it "calls #to_ary on the object when taking multiple arguments" do
|
||||
obj = mock("destructure block arguments")
|
||||
obj.should_receive(:to_ary).and_return([1, 2])
|
||||
|
||||
m(obj) { |a, b, c| [a, b, c] }.should == [1, 2, nil]
|
||||
end
|
||||
|
||||
it "does not call #to_ary when not taking any arguments" do
|
||||
obj = mock("destructure block arguments")
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
m(obj) { 1 }.should == 1
|
||||
end
|
||||
|
||||
it "does not call #to_ary on the object when taking a single argument" do
|
||||
obj = mock("destructure block arguments")
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
m(obj) { |a| a }.should == obj
|
||||
end
|
||||
|
||||
it "does not call #to_ary on the object when taking a single rest argument" do
|
||||
obj = mock("destructure block arguments")
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
m(obj) { |*a| a }.should == [obj]
|
||||
end
|
||||
|
||||
it "receives the object if #to_ary returns nil" do
|
||||
obj = mock("destructure block arguments")
|
||||
obj.should_receive(:to_ary).and_return(nil)
|
||||
|
||||
m(obj) { |a, b, c| [a, b, c] }.should == [obj, nil, nil]
|
||||
end
|
||||
|
||||
it "raises a TypeError if #to_ary does not return an Array" do
|
||||
obj = mock("destructure block arguments")
|
||||
obj.should_receive(:to_ary).and_return(1)
|
||||
|
||||
lambda { m(obj) { |a, b| } }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: rewrite
|
||||
describe "A block" do
|
||||
before :each do
|
||||
@y = BlockSpecs::Yielder.new
|
||||
end
|
||||
|
||||
it "captures locals from the surrounding scope" do
|
||||
var = 1
|
||||
@y.z { var }.should == 1
|
||||
end
|
||||
|
||||
it "allows for a leading space before the arguments" do
|
||||
res = @y.s (:a){ 1 }
|
||||
res.should == 1
|
||||
end
|
||||
|
||||
it "allows to define a block variable with the same name as the enclosing block" do
|
||||
o = BlockSpecs::OverwriteBlockVariable.new
|
||||
o.z { 1 }.should == 1
|
||||
end
|
||||
|
||||
it "does not capture a local when an argument has the same name" do
|
||||
var = 1
|
||||
@y.s(2) { |var| var }.should == 2
|
||||
var.should == 1
|
||||
end
|
||||
|
||||
it "does not capture a local when the block argument has the same name" do
|
||||
var = 1
|
||||
proc { |&var|
|
||||
var.call(2)
|
||||
}.call { |x| x }.should == 2
|
||||
var.should == 1
|
||||
end
|
||||
|
||||
describe "taking zero arguments" do
|
||||
it "does not raise an exception when no values are yielded" do
|
||||
@y.z { 1 }.should == 1
|
||||
end
|
||||
|
||||
it "does not raise an exception when values are yielded" do
|
||||
@y.s(0) { 1 }.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking || arguments" do
|
||||
it "does not raise an exception when no values are yielded" do
|
||||
@y.z { || 1 }.should == 1
|
||||
end
|
||||
|
||||
it "does not raise an exception when values are yielded" do
|
||||
@y.s(0) { || 1 }.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |a| arguments" do
|
||||
it "assigns nil to the argument when no values are yielded" do
|
||||
@y.z { |a| a }.should be_nil
|
||||
end
|
||||
|
||||
it "assigns the value yielded to the argument" do
|
||||
@y.s(1) { |a| a }.should == 1
|
||||
end
|
||||
|
||||
it "does not call #to_ary to convert a single yielded object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@y.s(obj) { |a| a }.should equal(obj)
|
||||
end
|
||||
|
||||
it "assigns the first value yielded to the argument" do
|
||||
@y.m(1, 2) { |a| a }.should == 1
|
||||
end
|
||||
|
||||
it "does not destructure a single Array value" do
|
||||
@y.s([1, 2]) { |a| a }.should == [1, 2]
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |a, b| arguments" do
|
||||
it "assgins nil to the arguments when no values are yielded" do
|
||||
@y.z { |a, b| [a, b] }.should == [nil, nil]
|
||||
end
|
||||
|
||||
it "assigns one value yielded to the first argument" do
|
||||
@y.s(1) { |a, b| [a, b] }.should == [1, nil]
|
||||
end
|
||||
|
||||
it "assigns the first two values yielded to the arguments" do
|
||||
@y.m(1, 2, 3) { |a, b| [a, b] }.should == [1, 2]
|
||||
end
|
||||
|
||||
it "does not destructure an Array value as one of several values yielded" do
|
||||
@y.m([1, 2], 3, 4) { |a, b| [a, b] }.should == [[1, 2], 3]
|
||||
end
|
||||
|
||||
it "assigns 'nil' and 'nil' to the arguments when a single, empty Array is yielded" do
|
||||
@y.s([]) { |a, b| [a, b] }.should == [nil, nil]
|
||||
end
|
||||
|
||||
it "assigns the element of a single element Array to the first argument" do
|
||||
@y.s([1]) { |a, b| [a, b] }.should == [1, nil]
|
||||
@y.s([nil]) { |a, b| [a, b] }.should == [nil, nil]
|
||||
@y.s([[]]) { |a, b| [a, b] }.should == [[], nil]
|
||||
end
|
||||
|
||||
it "destructures a single Array value yielded" do
|
||||
@y.s([1, 2, 3]) { |a, b| [a, b] }.should == [1, 2]
|
||||
end
|
||||
|
||||
it "destructures a splatted Array" do
|
||||
@y.r([[]]) { |a, b| [a, b] }.should == [nil, nil]
|
||||
@y.r([[1]]) { |a, b| [a, b] }.should == [1, nil]
|
||||
end
|
||||
|
||||
it "calls #to_ary to convert a single yielded object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_receive(:to_ary).and_return([1, 2])
|
||||
|
||||
@y.s(obj) { |a, b| [a, b] }.should == [1, 2]
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the single yielded object is an Array" do
|
||||
obj = [1, 2]
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@y.s(obj) { |a, b| [a, b] }.should == [1, 2]
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the object does not respond to #to_ary" do
|
||||
obj = mock("block yield no to_ary")
|
||||
|
||||
@y.s(obj) { |a, b| [a, b] }.should == [obj, nil]
|
||||
end
|
||||
|
||||
it "raises a TypeError if #to_ary does not return an Array" do
|
||||
obj = mock("block yield to_ary invalid")
|
||||
obj.should_receive(:to_ary).and_return(1)
|
||||
|
||||
lambda { @y.s(obj) { |a, b| } }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises the original exception if #to_ary raises an exception" do
|
||||
obj = mock("block yield to_ary raising an exception")
|
||||
obj.should_receive(:to_ary).and_raise(ZeroDivisionError)
|
||||
|
||||
lambda { @y.s(obj) { |a, b| } }.should raise_error(ZeroDivisionError)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "taking |a, *b| arguments" do
|
||||
it "assigns 'nil' and '[]' to the arguments when no values are yielded" do
|
||||
@y.z { |a, *b| [a, b] }.should == [nil, []]
|
||||
end
|
||||
|
||||
it "assigns all yielded values after the first to the rest argument" do
|
||||
@y.m(1, 2, 3) { |a, *b| [a, b] }.should == [1, [2, 3]]
|
||||
end
|
||||
|
||||
it "assigns 'nil' and '[]' to the arguments when a single, empty Array is yielded" do
|
||||
@y.s([]) { |a, *b| [a, b] }.should == [nil, []]
|
||||
end
|
||||
|
||||
it "assigns the element of a single element Array to the first argument" do
|
||||
@y.s([1]) { |a, *b| [a, b] }.should == [1, []]
|
||||
@y.s([nil]) { |a, *b| [a, b] }.should == [nil, []]
|
||||
@y.s([[]]) { |a, *b| [a, b] }.should == [[], []]
|
||||
end
|
||||
|
||||
it "destructures a splatted Array" do
|
||||
@y.r([[]]) { |a, *b| [a, b] }.should == [nil, []]
|
||||
@y.r([[1]]) { |a, *b| [a, b] }.should == [1, []]
|
||||
end
|
||||
|
||||
it "destructures a single Array value assigning the remaining values to the rest argument" do
|
||||
@y.s([1, 2, 3]) { |a, *b| [a, b] }.should == [1, [2, 3]]
|
||||
end
|
||||
|
||||
it "calls #to_ary to convert a single yielded object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_receive(:to_ary).and_return([1, 2])
|
||||
|
||||
@y.s(obj) { |a, *b| [a, b] }.should == [1, [2]]
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the single yielded object is an Array" do
|
||||
obj = [1, 2]
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@y.s(obj) { |a, *b| [a, b] }.should == [1, [2]]
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the object does not respond to #to_ary" do
|
||||
obj = mock("block yield no to_ary")
|
||||
|
||||
@y.s(obj) { |a, *b| [a, b] }.should == [obj, []]
|
||||
end
|
||||
|
||||
it "raises a TypeError if #to_ary does not return an Array" do
|
||||
obj = mock("block yield to_ary invalid")
|
||||
obj.should_receive(:to_ary).and_return(1)
|
||||
|
||||
lambda { @y.s(obj) { |a, *b| } }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |*| arguments" do
|
||||
it "does not raise an exception when no values are yielded" do
|
||||
@y.z { |*| 1 }.should == 1
|
||||
end
|
||||
|
||||
it "does not raise an exception when values are yielded" do
|
||||
@y.s(0) { |*| 1 }.should == 1
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the single yielded object is an Array" do
|
||||
obj = [1, 2]
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@y.s(obj) { |*| 1 }.should == 1
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the object does not respond to #to_ary" do
|
||||
obj = mock("block yield no to_ary")
|
||||
|
||||
@y.s(obj) { |*| 1 }.should == 1
|
||||
end
|
||||
|
||||
it "does not call #to_ary to convert a single yielded object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@y.s(obj) { |*| 1 }.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |*a| arguments" do
|
||||
it "assigns '[]' to the argument when no values are yielded" do
|
||||
@y.z { |*a| a }.should == []
|
||||
end
|
||||
|
||||
it "assigns a single value yielded to the argument as an Array" do
|
||||
@y.s(1) { |*a| a }.should == [1]
|
||||
end
|
||||
|
||||
it "assigns all the values passed to the argument as an Array" do
|
||||
@y.m(1, 2, 3) { |*a| a }.should == [1, 2, 3]
|
||||
end
|
||||
|
||||
it "assigns '[[]]' to the argument when passed an empty Array" do
|
||||
@y.s([]) { |*a| a }.should == [[]]
|
||||
end
|
||||
|
||||
it "assigns a single Array value passed to the argument by wrapping it in an Array" do
|
||||
@y.s([1, 2, 3]) { |*a| a }.should == [[1, 2, 3]]
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the single yielded object is an Array" do
|
||||
obj = [1, 2]
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@y.s(obj) { |*a| a }.should == [[1, 2]]
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the object does not respond to #to_ary" do
|
||||
obj = mock("block yield no to_ary")
|
||||
|
||||
@y.s(obj) { |*a| a }.should == [obj]
|
||||
end
|
||||
|
||||
it "does not call #to_ary to convert a single yielded object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@y.s(obj) { |*a| a }.should == [obj]
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |a, | arguments" do
|
||||
it "assigns nil to the argument when no values are yielded" do
|
||||
@y.z { |a, | a }.should be_nil
|
||||
end
|
||||
|
||||
it "assgins the argument a single value yielded" do
|
||||
@y.s(1) { |a, | a }.should == 1
|
||||
end
|
||||
|
||||
it "assigns the argument the first value yielded" do
|
||||
@y.m(1, 2) { |a, | a }.should == 1
|
||||
end
|
||||
|
||||
it "assigns the argument the first of several values yielded when it is an Array" do
|
||||
@y.m([1, 2], 3) { |a, | a }.should == [1, 2]
|
||||
end
|
||||
|
||||
it "assigns nil to the argument when passed an empty Array" do
|
||||
@y.s([]) { |a, | a }.should be_nil
|
||||
end
|
||||
|
||||
it "assigns the argument the first element of the Array when passed a single Array" do
|
||||
@y.s([1, 2]) { |a, | a }.should == 1
|
||||
end
|
||||
|
||||
it "calls #to_ary to convert a single yielded object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_receive(:to_ary).and_return([1, 2])
|
||||
|
||||
@y.s(obj) { |a, | a }.should == 1
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the single yielded object is an Array" do
|
||||
obj = [1, 2]
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@y.s(obj) { |a, | a }.should == 1
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the object does not respond to #to_ary" do
|
||||
obj = mock("block yield no to_ary")
|
||||
|
||||
@y.s(obj) { |a, | a }.should == obj
|
||||
end
|
||||
|
||||
it "raises a TypeError if #to_ary does not return an Array" do
|
||||
obj = mock("block yield to_ary invalid")
|
||||
obj.should_receive(:to_ary).and_return(1)
|
||||
|
||||
lambda { @y.s(obj) { |a, | } }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |(a, b)| arguments" do
|
||||
it "assigns nil to the arguments when yielded no values" do
|
||||
@y.z { |(a, b)| [a, b] }.should == [nil, nil]
|
||||
end
|
||||
|
||||
it "destructures a single Array value yielded" do
|
||||
@y.s([1, 2]) { |(a, b)| [a, b] }.should == [1, 2]
|
||||
end
|
||||
|
||||
it "destructures a single Array value yielded when shadowing an outer variable" do
|
||||
a = 9
|
||||
@y.s([1, 2]) { |(a, b)| [a, b] }.should == [1, 2]
|
||||
end
|
||||
|
||||
it "calls #to_ary to convert a single yielded object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_receive(:to_ary).and_return([1, 2])
|
||||
|
||||
@y.s(obj) { |(a, b)| [a, b] }.should == [1, 2]
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the single yielded object is an Array" do
|
||||
obj = [1, 2]
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@y.s(obj) { |(a, b)| [a, b] }.should == [1, 2]
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the object does not respond to #to_ary" do
|
||||
obj = mock("block yield no to_ary")
|
||||
|
||||
@y.s(obj) { |(a, b)| [a, b] }.should == [obj, nil]
|
||||
end
|
||||
|
||||
it "raises a TypeError if #to_ary does not return an Array" do
|
||||
obj = mock("block yield to_ary invalid")
|
||||
obj.should_receive(:to_ary).and_return(1)
|
||||
|
||||
lambda { @y.s(obj) { |(a, b)| } }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |(a, b), c| arguments" do
|
||||
it "assigns nil to the arguments when yielded no values" do
|
||||
@y.z { |(a, b), c| [a, b, c] }.should == [nil, nil, nil]
|
||||
end
|
||||
|
||||
it "destructures a single one-level Array value yielded" do
|
||||
@y.s([1, 2]) { |(a, b), c| [a, b, c] }.should == [1, nil, 2]
|
||||
end
|
||||
|
||||
it "destructures a single multi-level Array value yielded" do
|
||||
@y.s([[1, 2, 3], 4]) { |(a, b), c| [a, b, c] }.should == [1, 2, 4]
|
||||
end
|
||||
|
||||
it "calls #to_ary to convert a single yielded object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_receive(:to_ary).and_return([1, 2])
|
||||
|
||||
@y.s(obj) { |(a, b), c| [a, b, c] }.should == [1, nil, 2]
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the single yielded object is an Array" do
|
||||
obj = [1, 2]
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@y.s(obj) { |(a, b), c| [a, b, c] }.should == [1, nil, 2]
|
||||
end
|
||||
|
||||
it "does not call #to_ary if the object does not respond to #to_ary" do
|
||||
obj = mock("block yield no to_ary")
|
||||
|
||||
@y.s(obj) { |(a, b), c| [a, b, c] }.should == [obj, nil, nil]
|
||||
end
|
||||
|
||||
it "raises a TypeError if #to_ary does not return an Array" do
|
||||
obj = mock("block yield to_ary invalid")
|
||||
obj.should_receive(:to_ary).and_return(1)
|
||||
|
||||
lambda { @y.s(obj) { |(a, b), c| } }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking nested |a, (b, (c, d))|" do
|
||||
it "assigns nil to the arguments when yielded no values" do
|
||||
@y.m { |a, (b, (c, d))| [a, b, c, d] }.should == [nil, nil, nil, nil]
|
||||
end
|
||||
|
||||
it "destructures separate yielded values" do
|
||||
@y.m(1, 2) { |a, (b, (c, d))| [a, b, c, d] }.should == [1, 2, nil, nil]
|
||||
end
|
||||
|
||||
it "destructures a nested Array value yielded" do
|
||||
@y.m(1, [2, 3]) { |a, (b, (c, d))| [a, b, c, d] }.should == [1, 2, 3, nil]
|
||||
end
|
||||
|
||||
it "destructures a single multi-level Array value yielded" do
|
||||
@y.m(1, [2, [3, 4]]) { |a, (b, (c, d))| [a, b, c, d] }.should == [1, 2, 3, 4]
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking nested |a, ((b, c), d)|" do
|
||||
it "assigns nil to the arguments when yielded no values" do
|
||||
@y.m { |a, ((b, c), d)| [a, b, c, d] }.should == [nil, nil, nil, nil]
|
||||
end
|
||||
|
||||
it "destructures separate yielded values" do
|
||||
@y.m(1, 2) { |a, ((b, c), d)| [a, b, c, d] }.should == [1, 2, nil, nil]
|
||||
end
|
||||
|
||||
it "destructures a nested value yielded" do
|
||||
@y.m(1, [2, 3]) { |a, ((b, c), d)| [a, b, c, d] }.should == [1, 2, nil, 3]
|
||||
end
|
||||
|
||||
it "destructures a single multi-level Array value yielded" do
|
||||
@y.m(1, [[2, 3], 4]) { |a, ((b, c), d)| [a, b, c, d] }.should == [1, 2, 3, 4]
|
||||
end
|
||||
end
|
||||
|
||||
describe "arguments with _" do
|
||||
it "extracts arguments with _" do
|
||||
@y.m([[1, 2, 3], 4]) { |(_, a, _), _| a }.should == 2
|
||||
@y.m([1, [2, 3, 4]]) { |_, (_, a, _)| a }.should == 3
|
||||
end
|
||||
|
||||
it "assigns the first variable named" do
|
||||
@y.m(1, 2) { |_, _| _ }.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking identically-named arguments" do
|
||||
it "raises a SyntaxError for standard arguments" do
|
||||
lambda { eval "lambda { |x,x| }" }.should raise_error(SyntaxError)
|
||||
lambda { eval "->(x,x) {}" }.should raise_error(SyntaxError)
|
||||
lambda { eval "Proc.new { |x,x| }" }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "accepts unnamed arguments" do
|
||||
eval("lambda { |_,_| }").should be_an_instance_of(Proc)
|
||||
eval("->(_,_) {}").should be_an_instance_of(Proc)
|
||||
eval("Proc.new { |_,_| }").should be_an_instance_of(Proc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Block-local variables" do
|
||||
it "are introduced with a semi-colon in the parameter list" do
|
||||
[1].map {|one; bl| bl }.should == [nil]
|
||||
end
|
||||
|
||||
it "can be specified in a comma-separated list after the semi-colon" do
|
||||
[1].map {|one; bl, bl2| [bl, bl2] }.should == [[nil, nil]]
|
||||
end
|
||||
|
||||
it "can not have the same name as one of the standard parameters" do
|
||||
lambda { eval "[1].each {|foo; foo| }" }.should raise_error(SyntaxError)
|
||||
lambda { eval "[1].each {|foo, bar; glark, bar| }" }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "can not be prefixed with an asterisk" do
|
||||
lambda { eval "[1].each {|foo; *bar| }" }.should raise_error(SyntaxError)
|
||||
lambda do
|
||||
eval "[1].each {|foo, bar; glark, *fnord| }"
|
||||
end.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "can not be prefixed with an ampersand" do
|
||||
lambda { eval "[1].each {|foo; &bar| }" }.should raise_error(SyntaxError)
|
||||
lambda do
|
||||
eval "[1].each {|foo, bar; glark, &fnord| }"
|
||||
end.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "can not be assigned default values" do
|
||||
lambda { eval "[1].each {|foo; bar=1| }" }.should raise_error(SyntaxError)
|
||||
lambda do
|
||||
eval "[1].each {|foo, bar; glark, fnord=:fnord| }"
|
||||
end.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "need not be preceeded by standard parameters" do
|
||||
[1].map {|; foo| foo }.should == [nil]
|
||||
[1].map {|; glark, bar| [glark, bar] }.should == [[nil, nil]]
|
||||
end
|
||||
|
||||
it "only allow a single semi-colon in the parameter list" do
|
||||
lambda { eval "[1].each {|foo; bar; glark| }" }.should raise_error(SyntaxError)
|
||||
lambda { eval "[1].each {|; bar; glark| }" }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "override shadowed variables from the outer scope" do
|
||||
out = :out
|
||||
[1].each {|; out| out = :in }
|
||||
out.should == :out
|
||||
|
||||
a = :a
|
||||
b = :b
|
||||
c = :c
|
||||
d = :d
|
||||
{ant: :bee}.each_pair do |a, b; c, d|
|
||||
a = :A
|
||||
b = :B
|
||||
c = :C
|
||||
d = :D
|
||||
end
|
||||
a.should == :a
|
||||
b.should == :b
|
||||
c.should == :c
|
||||
d.should == :d
|
||||
end
|
||||
|
||||
it "are not automatically instantiated in the outer scope" do
|
||||
defined?(glark).should be_nil
|
||||
[1].each {|;glark| 1}
|
||||
defined?(glark).should be_nil
|
||||
end
|
||||
|
||||
it "are automatically instantiated in the block" do
|
||||
[1].each do |;glark|
|
||||
glark.should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it "are visible in deeper scopes before initialization" do
|
||||
[1].each {|;glark|
|
||||
[1].each {
|
||||
defined?(glark).should_not be_nil
|
||||
glark = 1
|
||||
}
|
||||
glark.should == 1
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "Post-args" do
|
||||
it "appear after a splat" do
|
||||
proc do |*a, b|
|
||||
[a, b]
|
||||
end.call(1, 2, 3).should == [[1, 2], 3]
|
||||
|
||||
proc do |*a, b, c|
|
||||
[a, b, c]
|
||||
end.call(1, 2, 3).should == [[1], 2, 3]
|
||||
|
||||
proc do |*a, b, c, d|
|
||||
[a, b, c, d]
|
||||
end.call(1, 2, 3).should == [[], 1, 2, 3]
|
||||
end
|
||||
|
||||
it "are required" do
|
||||
lambda {
|
||||
lambda do |*a, b|
|
||||
[a, b]
|
||||
end.call
|
||||
}.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
describe "with required args" do
|
||||
|
||||
it "gathers remaining args in the splat" do
|
||||
proc do |a, *b, c|
|
||||
[a, b, c]
|
||||
end.call(1, 2, 3).should == [1, [2], 3]
|
||||
end
|
||||
|
||||
it "has an empty splat when there are no remaining args" do
|
||||
proc do |a, b, *c, d|
|
||||
[a, b, c, d]
|
||||
end.call(1, 2, 3).should == [1, 2, [], 3]
|
||||
|
||||
proc do |a, *b, c, d|
|
||||
[a, b, c, d]
|
||||
end.call(1, 2, 3).should == [1, [], 2, 3]
|
||||
end
|
||||
end
|
||||
|
||||
describe "with optional args" do
|
||||
|
||||
it "gathers remaining args in the splat" do
|
||||
proc do |a=5, *b, c|
|
||||
[a, b, c]
|
||||
end.call(1, 2, 3).should == [1, [2], 3]
|
||||
end
|
||||
|
||||
it "overrides the optional arg before gathering in the splat" do
|
||||
proc do |a=5, *b, c|
|
||||
[a, b, c]
|
||||
end.call(2, 3).should == [2, [], 3]
|
||||
|
||||
proc do |a=5, b=6, *c, d|
|
||||
[a, b, c, d]
|
||||
end.call(1, 2, 3).should == [1, 2, [], 3]
|
||||
|
||||
proc do |a=5, *b, c, d|
|
||||
[a, b, c, d]
|
||||
end.call(1, 2, 3).should == [1, [], 2, 3]
|
||||
end
|
||||
|
||||
it "uses the required arg before the optional and the splat" do
|
||||
proc do |a=5, *b, c|
|
||||
[a, b, c]
|
||||
end.call(3).should == [5, [], 3]
|
||||
|
||||
proc do |a=5, b=6, *c, d|
|
||||
[a, b, c, d]
|
||||
end.call(3).should == [5, 6, [], 3]
|
||||
|
||||
proc do |a=5, *b, c, d|
|
||||
[a, b, c, d]
|
||||
end.call(2, 3).should == [5, [], 2, 3]
|
||||
end
|
||||
|
||||
it "overrides the optional args from left to right before gathering the splat" do
|
||||
proc do |a=5, b=6, *c, d|
|
||||
[a, b, c, d]
|
||||
end.call(2, 3).should == [2, 6, [], 3]
|
||||
end
|
||||
|
||||
describe "with a circular argument reference" do
|
||||
it "shadows an existing local with the same name as the argument" do
|
||||
a = 1
|
||||
-> {
|
||||
@proc = eval "proc { |a=a| a }"
|
||||
}.should complain(/circular argument reference/)
|
||||
@proc.call.should == nil
|
||||
end
|
||||
|
||||
it "shadows an existing method with the same name as the argument" do
|
||||
def a; 1; end
|
||||
-> {
|
||||
@proc = eval "proc { |a=a| a }"
|
||||
}.should complain(/circular argument reference/)
|
||||
@proc.call.should == nil
|
||||
end
|
||||
|
||||
it "calls an existing method with the same name as the argument if explicitly using ()" do
|
||||
def a; 1; end
|
||||
proc { |a=a()| a }.call.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with pattern matching" do
|
||||
it "extracts matched blocks with post arguments" do
|
||||
proc do |(a, *b, c), d, e|
|
||||
[a, b, c, d, e]
|
||||
end.call([1, 2, 3, 4], 5, 6).should == [1, [2, 3], 4, 5, 6]
|
||||
end
|
||||
|
||||
it "allows empty splats" do
|
||||
proc do |a, (*), b|
|
||||
[a, b]
|
||||
end.call([1, 2, 3]).should == [1, 3]
|
||||
end
|
||||
end
|
||||
end
|
348
spec/ruby/language/break_spec.rb
Normal file
348
spec/ruby/language/break_spec.rb
Normal file
|
@ -0,0 +1,348 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/break', __FILE__)
|
||||
|
||||
describe "The break statement in a block" do
|
||||
before :each do
|
||||
ScratchPad.record []
|
||||
@program = BreakSpecs::Block.new
|
||||
end
|
||||
|
||||
it "returns nil to method invoking the method yielding to the block when not passed an argument" do
|
||||
@program.break_nil
|
||||
ScratchPad.recorded.should == [:a, :aa, :b, nil, :d]
|
||||
end
|
||||
|
||||
it "returns a value to the method invoking the method yielding to the block" do
|
||||
@program.break_value
|
||||
ScratchPad.recorded.should == [:a, :aa, :b, :break, :d]
|
||||
end
|
||||
|
||||
describe "yielded inside a while" do
|
||||
it "breaks out of the block" do
|
||||
value = @program.break_in_block_in_while
|
||||
ScratchPad.recorded.should == [:aa, :break]
|
||||
value.should == :value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "The break statement in a captured block" do
|
||||
before :each do
|
||||
ScratchPad.record []
|
||||
@program = BreakSpecs::Block.new
|
||||
end
|
||||
|
||||
describe "when the invocation of the scope creating the block is still active" do
|
||||
it "raises a LocalJumpError when invoking the block from the scope creating the block" do
|
||||
lambda { @program.break_in_method }.should raise_error(LocalJumpError)
|
||||
ScratchPad.recorded.should == [:a, :xa, :d, :b]
|
||||
end
|
||||
|
||||
it "raises a LocalJumpError when invoking the block from a method" do
|
||||
lambda { @program.break_in_nested_method }.should raise_error(LocalJumpError)
|
||||
ScratchPad.recorded.should == [:a, :xa, :cc, :aa, :b]
|
||||
end
|
||||
|
||||
it "raises a LocalJumpError when yielding to the block" do
|
||||
lambda { @program.break_in_yielding_method }.should raise_error(LocalJumpError)
|
||||
ScratchPad.recorded.should == [:a, :xa, :cc, :aa, :b]
|
||||
end
|
||||
end
|
||||
|
||||
describe "from a scope that has returned" do
|
||||
it "raises a LocalJumpError when calling the block from a method" do
|
||||
lambda { @program.break_in_method_captured }.should raise_error(LocalJumpError)
|
||||
ScratchPad.recorded.should == [:a, :za, :xa, :zd, :zb]
|
||||
end
|
||||
|
||||
it "raises a LocalJumpError when yielding to the block" do
|
||||
lambda { @program.break_in_yield_captured }.should raise_error(LocalJumpError)
|
||||
ScratchPad.recorded.should == [:a, :za, :xa, :zd, :aa, :zb]
|
||||
end
|
||||
end
|
||||
|
||||
describe "from another thread" do
|
||||
it "raises a LocalJumpError when getting the value from another thread" do
|
||||
ScratchPad << :a
|
||||
thread_with_break = Thread.new do
|
||||
ScratchPad << :b
|
||||
break :break
|
||||
ScratchPad << :c
|
||||
end
|
||||
|
||||
lambda { thread_with_break.value }.should raise_error(LocalJumpError)
|
||||
ScratchPad.recorded.should == [:a, :b]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "The break statement in a lambda" do
|
||||
before :each do
|
||||
ScratchPad.record []
|
||||
@program = BreakSpecs::Lambda.new
|
||||
end
|
||||
|
||||
it "returns from the lambda" do
|
||||
l = lambda {
|
||||
ScratchPad << :before
|
||||
break :foo
|
||||
ScratchPad << :after
|
||||
}
|
||||
l.call.should == :foo
|
||||
ScratchPad.recorded.should == [:before]
|
||||
end
|
||||
|
||||
it "returns from the call site if the lambda is passed as a block" do
|
||||
def mid(&b)
|
||||
lambda {
|
||||
ScratchPad << :before
|
||||
b.call
|
||||
ScratchPad << :unreachable1
|
||||
}.call
|
||||
ScratchPad << :unreachable2
|
||||
end
|
||||
|
||||
result = [1].each do |e|
|
||||
mid {
|
||||
break # This breaks from mid
|
||||
ScratchPad << :unreachable3
|
||||
}
|
||||
ScratchPad << :after
|
||||
end
|
||||
result.should == [1]
|
||||
ScratchPad.recorded.should == [:before, :after]
|
||||
end
|
||||
|
||||
describe "when the invocation of the scope creating the lambda is still active" do
|
||||
it "returns nil when not passed an argument" do
|
||||
@program.break_in_defining_scope false
|
||||
ScratchPad.recorded.should == [:a, :b, nil, :d]
|
||||
end
|
||||
|
||||
it "returns a value to the scope creating and calling the lambda" do
|
||||
@program.break_in_defining_scope
|
||||
ScratchPad.recorded.should == [:a, :b, :break, :d]
|
||||
end
|
||||
|
||||
it "returns a value to the method scope below invoking the lambda" do
|
||||
@program.break_in_nested_scope
|
||||
ScratchPad.recorded.should == [:a, :d, :aa, :b, :break, :bb, :e]
|
||||
end
|
||||
|
||||
it "returns a value to a block scope invoking the lambda in a method below" do
|
||||
@program.break_in_nested_scope_block
|
||||
ScratchPad.recorded.should == [:a, :d, :aa, :aaa, :bb, :b, :break, :cc, :bbb, :dd, :e]
|
||||
end
|
||||
|
||||
it "returns from the lambda" do
|
||||
@program.break_in_nested_scope_yield
|
||||
ScratchPad.recorded.should == [:a, :d, :aaa, :b, :bbb, :e]
|
||||
end
|
||||
end
|
||||
|
||||
describe "created at the toplevel" do
|
||||
it "returns a value when invoking from the toplevel" do
|
||||
code = fixture __FILE__, "break_lambda_toplevel.rb"
|
||||
ruby_exe(code).chomp.should == "a,b,break,d"
|
||||
end
|
||||
|
||||
it "returns a value when invoking from a method" do
|
||||
code = fixture __FILE__, "break_lambda_toplevel_method.rb"
|
||||
ruby_exe(code).chomp.should == "a,d,b,break,e,f"
|
||||
end
|
||||
|
||||
it "returns a value when invoking from a block" do
|
||||
code = fixture __FILE__, "break_lambda_toplevel_block.rb"
|
||||
ruby_exe(code).chomp.should == "a,d,f,b,break,g,e,h"
|
||||
end
|
||||
end
|
||||
|
||||
describe "from a scope that has returned" do
|
||||
it "returns a value to the method scope invoking the lambda" do
|
||||
@program.break_in_method
|
||||
ScratchPad.recorded.should == [:a, :la, :ld, :lb, :break, :b]
|
||||
end
|
||||
|
||||
it "returns a value to the block scope invoking the lambda in a method" do
|
||||
@program.break_in_block_in_method
|
||||
ScratchPad.recorded.should == [:a, :aaa, :b, :la, :ld, :lb, :break, :c, :bbb, :d]
|
||||
end
|
||||
|
||||
# By passing a lambda as a block argument, the user is requesting to treat
|
||||
# the lambda as a block, which in this case means breaking to a scope that
|
||||
# has returned. This is a subtle and confusing semantic where a block pass
|
||||
# is removing the lambda-ness of a lambda.
|
||||
it "raises a LocalJumpError when yielding to a lambda passed as a block argument" do
|
||||
@program.break_in_method_yield
|
||||
ScratchPad.recorded.should == [:a, :la, :ld, :aaa, :lb, :bbb, :b]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Break inside a while loop" do
|
||||
describe "with a value" do
|
||||
it "exits the loop and returns the value" do
|
||||
a = while true; break; end; a.should == nil
|
||||
a = while true; break nil; end; a.should == nil
|
||||
a = while true; break 1; end; a.should == 1
|
||||
a = while true; break []; end; a.should == []
|
||||
a = while true; break [1]; end; a.should == [1]
|
||||
end
|
||||
|
||||
it "passes the value returned by a method with omitted parenthesis and passed block" do
|
||||
obj = BreakSpecs::Block.new
|
||||
lambda { break obj.method :value do |x| x end }.call.should == :value
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a splat" do
|
||||
it "exits the loop and makes the splat an Array" do
|
||||
a = while true; break *[1,2]; end; a.should == [1,2]
|
||||
end
|
||||
|
||||
it "treats nil as an empty array" do
|
||||
a = while true; break *nil; end; a.should == []
|
||||
end
|
||||
|
||||
it "preserves an array as is" do
|
||||
a = while true; break *[]; end; a.should == []
|
||||
a = while true; break *[1,2]; end; a.should == [1,2]
|
||||
a = while true; break *[nil]; end; a.should == [nil]
|
||||
a = while true; break *[[]]; end; a.should == [[]]
|
||||
end
|
||||
|
||||
it "wraps a non-Array in an Array" do
|
||||
a = while true; break *1; end; a.should == [1]
|
||||
end
|
||||
end
|
||||
|
||||
it "stops a while loop when run" do
|
||||
i = 0
|
||||
while true
|
||||
break if i == 2
|
||||
i+=1
|
||||
end
|
||||
i.should == 2
|
||||
end
|
||||
|
||||
it "causes a call with a block to return when run" do
|
||||
at = 0
|
||||
0.upto(5) do |i|
|
||||
at = i
|
||||
break i if i == 2
|
||||
end.should == 2
|
||||
at.should == 2
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# TODO: Rewrite all the specs from here to the end of the file in the style
|
||||
# above.
|
||||
describe "Executing break from within a block" do
|
||||
|
||||
before :each do
|
||||
ScratchPad.clear
|
||||
end
|
||||
|
||||
# Discovered in JRuby (see JRUBY-2756)
|
||||
it "returns from the original invoking method even in case of chained calls" do
|
||||
class BreakTest
|
||||
# case #1: yield
|
||||
def self.meth_with_yield(&b)
|
||||
yield
|
||||
fail("break returned from yield to wrong place")
|
||||
end
|
||||
def self.invoking_method(&b)
|
||||
meth_with_yield(&b)
|
||||
fail("break returned from 'meth_with_yield' method to wrong place")
|
||||
end
|
||||
|
||||
# case #2: block.call
|
||||
def self.meth_with_block_call(&b)
|
||||
b.call
|
||||
fail("break returned from b.call to wrong place")
|
||||
end
|
||||
def self.invoking_method2(&b)
|
||||
meth_with_block_call(&b)
|
||||
fail("break returned from 'meth_with_block_call' method to wrong place")
|
||||
end
|
||||
end
|
||||
|
||||
# this calls a method that calls another method that yields to the block
|
||||
BreakTest.invoking_method do
|
||||
break
|
||||
fail("break didn't, well, break")
|
||||
end
|
||||
|
||||
# this calls a method that calls another method that calls the block
|
||||
BreakTest.invoking_method2 do
|
||||
break
|
||||
fail("break didn't, well, break")
|
||||
end
|
||||
|
||||
res = BreakTest.invoking_method do
|
||||
break :return_value
|
||||
fail("break didn't, well, break")
|
||||
end
|
||||
res.should == :return_value
|
||||
|
||||
res = BreakTest.invoking_method2 do
|
||||
break :return_value
|
||||
fail("break didn't, well, break")
|
||||
end
|
||||
res.should == :return_value
|
||||
|
||||
end
|
||||
|
||||
class BreakTest2
|
||||
def one
|
||||
two { yield }
|
||||
end
|
||||
|
||||
def two
|
||||
yield
|
||||
ensure
|
||||
ScratchPad << :two_ensure
|
||||
end
|
||||
|
||||
def three
|
||||
begin
|
||||
one { break }
|
||||
ScratchPad << :three_post
|
||||
ensure
|
||||
ScratchPad << :three_ensure
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "runs ensures when continuing upward" do
|
||||
ScratchPad.record []
|
||||
|
||||
bt2 = BreakTest2.new
|
||||
bt2.one { break }
|
||||
ScratchPad.recorded.should == [:two_ensure]
|
||||
end
|
||||
|
||||
it "runs ensures when breaking from a loop" do
|
||||
ScratchPad.record []
|
||||
|
||||
while true
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
break if true
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
|
||||
it "doesn't run ensures in the destination method" do
|
||||
ScratchPad.record []
|
||||
|
||||
bt2 = BreakTest2.new
|
||||
bt2.three
|
||||
ScratchPad.recorded.should == [:two_ensure, :three_post, :three_ensure]
|
||||
end
|
||||
end
|
382
spec/ruby/language/case_spec.rb
Normal file
382
spec/ruby/language/case_spec.rb
Normal file
|
@ -0,0 +1,382 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "The 'case'-construct" do
|
||||
it "evaluates the body of the when clause matching the case target expression" do
|
||||
case 1
|
||||
when 2; false
|
||||
when 1; true
|
||||
end.should == true
|
||||
end
|
||||
|
||||
it "evaluates the body of the when clause whose array expression includes the case target expression" do
|
||||
case 2
|
||||
when 3, 4; false
|
||||
when 1, 2; true
|
||||
end.should == true
|
||||
end
|
||||
|
||||
it "evaluates the body of the when clause in left-to-right order if it's an array expression" do
|
||||
@calls = []
|
||||
def foo; @calls << :foo; end
|
||||
def bar; @calls << :bar; end
|
||||
|
||||
case true
|
||||
when foo, bar;
|
||||
end
|
||||
|
||||
@calls.should == [:foo, :bar]
|
||||
end
|
||||
|
||||
it "evaluates the body of the when clause whose range expression includes the case target expression" do
|
||||
case 5
|
||||
when 21..30; false
|
||||
when 1..20; true
|
||||
end.should == true
|
||||
end
|
||||
|
||||
it "returns nil when no 'then'-bodies are given" do
|
||||
case "a"
|
||||
when "a"
|
||||
when "b"
|
||||
end.should == nil
|
||||
end
|
||||
|
||||
it "evaluates the 'else'-body when no other expression matches" do
|
||||
case "c"
|
||||
when "a"; 'foo'
|
||||
when "b"; 'bar'
|
||||
else 'zzz'
|
||||
end.should == 'zzz'
|
||||
end
|
||||
|
||||
it "returns nil when no expression matches and 'else'-body is empty" do
|
||||
case "c"
|
||||
when "a"; "a"
|
||||
when "b"; "b"
|
||||
else
|
||||
end.should == nil
|
||||
end
|
||||
|
||||
it "returns 2 when a then body is empty" do
|
||||
case Object.new
|
||||
when Numeric then
|
||||
1
|
||||
when String then
|
||||
# ok
|
||||
else
|
||||
2
|
||||
end.should == 2
|
||||
end
|
||||
|
||||
it "returns the statement following 'then'" do
|
||||
case "a"
|
||||
when "a" then 'foo'
|
||||
when "b" then 'bar'
|
||||
end.should == 'foo'
|
||||
end
|
||||
|
||||
it "tests classes with case equality" do
|
||||
case "a"
|
||||
when String
|
||||
'foo'
|
||||
when Symbol
|
||||
'bar'
|
||||
end.should == 'foo'
|
||||
end
|
||||
|
||||
it "tests with matching regexps" do
|
||||
case "hello"
|
||||
when /abc/; false
|
||||
when /^hell/; true
|
||||
end.should == true
|
||||
end
|
||||
|
||||
it "does not test with equality when given classes" do
|
||||
case :symbol.class
|
||||
when Symbol
|
||||
"bar"
|
||||
when String
|
||||
"bar"
|
||||
else
|
||||
"foo"
|
||||
end.should == "foo"
|
||||
end
|
||||
|
||||
it "takes lists of values" do
|
||||
case 'z'
|
||||
when 'a', 'b', 'c', 'd'
|
||||
"foo"
|
||||
when 'x', 'y', 'z'
|
||||
"bar"
|
||||
end.should == "bar"
|
||||
|
||||
case 'b'
|
||||
when 'a', 'b', 'c', 'd'
|
||||
"foo"
|
||||
when 'x', 'y', 'z'
|
||||
"bar"
|
||||
end.should == "foo"
|
||||
end
|
||||
|
||||
it "expands arrays to lists of values" do
|
||||
case 'z'
|
||||
when *['a', 'b', 'c', 'd']
|
||||
"foo"
|
||||
when *['x', 'y', 'z']
|
||||
"bar"
|
||||
end.should == "bar"
|
||||
end
|
||||
|
||||
it "takes an expanded array in addition to a list of values" do
|
||||
case 'f'
|
||||
when 'f', *['a', 'b', 'c', 'd']
|
||||
"foo"
|
||||
when *['x', 'y', 'z']
|
||||
"bar"
|
||||
end.should == "foo"
|
||||
|
||||
case 'b'
|
||||
when 'f', *['a', 'b', 'c', 'd']
|
||||
"foo"
|
||||
when *['x', 'y', 'z']
|
||||
"bar"
|
||||
end.should == "foo"
|
||||
end
|
||||
|
||||
it "takes an expanded array before additional listed values" do
|
||||
case 'f'
|
||||
when *['a', 'b', 'c', 'd'], 'f'
|
||||
"foo"
|
||||
when *['x', 'y', 'z']
|
||||
"bar"
|
||||
end.should == 'foo'
|
||||
end
|
||||
|
||||
it "expands arrays from variables before additional listed values" do
|
||||
a = ['a', 'b', 'c']
|
||||
case 'a'
|
||||
when *a, 'd', 'e'
|
||||
"foo"
|
||||
when 'x'
|
||||
"bar"
|
||||
end.should == "foo"
|
||||
end
|
||||
|
||||
it "expands arrays from variables before a single additional listed value" do
|
||||
a = ['a', 'b', 'c']
|
||||
case 'a'
|
||||
when *a, 'd'
|
||||
"foo"
|
||||
when 'x'
|
||||
"bar"
|
||||
end.should == "foo"
|
||||
end
|
||||
|
||||
it "expands multiple arrays from variables before additional listed values" do
|
||||
a = ['a', 'b', 'c']
|
||||
b = ['d', 'e', 'f']
|
||||
|
||||
case 'f'
|
||||
when *a, *b, 'g', 'h'
|
||||
"foo"
|
||||
when 'x'
|
||||
"bar"
|
||||
end.should == "foo"
|
||||
end
|
||||
|
||||
# MR: critical
|
||||
it "concats arrays before expanding them" do
|
||||
a = ['a', 'b', 'c', 'd']
|
||||
b = ['f']
|
||||
|
||||
case 'f'
|
||||
when 'f', *a|b
|
||||
"foo"
|
||||
when *['x', 'y', 'z']
|
||||
"bar"
|
||||
end.should == "foo"
|
||||
end
|
||||
|
||||
it "never matches when clauses with no values" do
|
||||
case nil
|
||||
when *[]
|
||||
"foo"
|
||||
end.should == nil
|
||||
end
|
||||
|
||||
it "lets you define a method after the case statement" do
|
||||
case (def foo; 'foo'; end; 'f')
|
||||
when 'a'
|
||||
'foo'
|
||||
when 'f'
|
||||
'bar'
|
||||
end.should == 'bar'
|
||||
end
|
||||
|
||||
it "raises a SyntaxError when 'else' is used when no 'when' is given" do
|
||||
lambda {
|
||||
eval <<-CODE
|
||||
case 4
|
||||
else
|
||||
true
|
||||
end
|
||||
CODE
|
||||
}.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "raises a SyntaxError when 'else' is used before a 'when' was given" do
|
||||
lambda {
|
||||
eval <<-CODE
|
||||
case 4
|
||||
else
|
||||
true
|
||||
when 4; false
|
||||
end
|
||||
CODE
|
||||
}.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "supports nested case statements" do
|
||||
result = false
|
||||
case :x
|
||||
when Symbol
|
||||
case :y
|
||||
when Symbol
|
||||
result = true
|
||||
end
|
||||
end
|
||||
result.should == true
|
||||
end
|
||||
|
||||
it "supports nested case statements followed by a when with a splatted array" do
|
||||
result = false
|
||||
case :x
|
||||
when Symbol
|
||||
case :y
|
||||
when Symbol
|
||||
result = true
|
||||
end
|
||||
when *[Symbol]
|
||||
result = false
|
||||
end
|
||||
result.should == true
|
||||
end
|
||||
|
||||
it "supports nested case statements followed by a when with a splatted non-array" do
|
||||
result = false
|
||||
case :x
|
||||
when Symbol
|
||||
case :y
|
||||
when Symbol
|
||||
result = true
|
||||
end
|
||||
when *Symbol
|
||||
result = false
|
||||
end
|
||||
result.should == true
|
||||
end
|
||||
|
||||
it "works even if there's only one when statement" do
|
||||
case 1
|
||||
when 1
|
||||
100
|
||||
end.should == 100
|
||||
end
|
||||
end
|
||||
|
||||
describe "The 'case'-construct with no target expression" do
|
||||
it "evaluates the body of the first clause when at least one of its condition expressions is true" do
|
||||
case
|
||||
when true, false; 'foo'
|
||||
end.should == 'foo'
|
||||
end
|
||||
|
||||
it "evaluates the body of the first when clause that is not false/nil" do
|
||||
case
|
||||
when false; 'foo'
|
||||
when 2; 'bar'
|
||||
when 1 == 1; 'baz'
|
||||
end.should == 'bar'
|
||||
|
||||
case
|
||||
when false; 'foo'
|
||||
when nil; 'foo'
|
||||
when 1 == 1; 'bar'
|
||||
end.should == 'bar'
|
||||
end
|
||||
|
||||
it "evaluates the body of the else clause if all when clauses are false/nil" do
|
||||
case
|
||||
when false; 'foo'
|
||||
when nil; 'foo'
|
||||
when 1 == 2; 'bar'
|
||||
else 'baz'
|
||||
end.should == 'baz'
|
||||
end
|
||||
|
||||
it "evaluates multiple conditional expressions as a boolean disjunction" do
|
||||
case
|
||||
when true, false; 'foo'
|
||||
else 'bar'
|
||||
end.should == 'foo'
|
||||
|
||||
case
|
||||
when false, true; 'foo'
|
||||
else 'bar'
|
||||
end.should == 'foo'
|
||||
end
|
||||
|
||||
it "evaluates true as only 'true' when true is the first clause" do
|
||||
case 1
|
||||
when true; "bad"
|
||||
when Integer; "good"
|
||||
end.should == "good"
|
||||
end
|
||||
|
||||
it "evaluates false as only 'false' when false is the first clause" do
|
||||
case nil
|
||||
when false; "bad"
|
||||
when nil; "good"
|
||||
end.should == "good"
|
||||
end
|
||||
|
||||
it "treats a literal array as its own when argument, rather than a list of arguments" do
|
||||
case 'foo'
|
||||
when ['foo', 'foo']; 'bad'
|
||||
when 'foo'; 'good'
|
||||
end.should == 'good'
|
||||
end
|
||||
|
||||
it "takes multiple expanded arrays" do
|
||||
a1 = ['f', 'o', 'o']
|
||||
a2 = ['b', 'a', 'r']
|
||||
|
||||
case 'f'
|
||||
when *a1, *['x', 'y', 'z']
|
||||
"foo"
|
||||
when *a2, *['x', 'y', 'z']
|
||||
"bar"
|
||||
end.should == "foo"
|
||||
|
||||
case 'b'
|
||||
when *a1, *['x', 'y', 'z']
|
||||
"foo"
|
||||
when *a2, *['x', 'y', 'z']
|
||||
"bar"
|
||||
end.should == "bar"
|
||||
end
|
||||
|
||||
it "calls === even when private" do
|
||||
klass = Class.new do
|
||||
def ===(o)
|
||||
true
|
||||
end
|
||||
private :===
|
||||
end
|
||||
|
||||
case 1
|
||||
when klass.new
|
||||
:called
|
||||
end.should == :called
|
||||
end
|
||||
end
|
332
spec/ruby/language/class_spec.rb
Normal file
332
spec/ruby/language/class_spec.rb
Normal file
|
@ -0,0 +1,332 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/class', __FILE__)
|
||||
|
||||
ClassSpecsNumber = 12
|
||||
|
||||
module ClassSpecs
|
||||
Number = 12
|
||||
end
|
||||
|
||||
describe "The class keyword" do
|
||||
it "creates a new class with semicolon" do
|
||||
class ClassSpecsKeywordWithSemicolon; end
|
||||
ClassSpecsKeywordWithSemicolon.should be_an_instance_of(Class)
|
||||
end
|
||||
|
||||
ruby_version_is "2.3" do
|
||||
it "does not raise a SyntaxError when opening a class without a semicolon" do
|
||||
eval "class ClassSpecsKeywordWithoutSemicolon end"
|
||||
ClassSpecsKeywordWithoutSemicolon.should be_an_instance_of(Class)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "A class definition" do
|
||||
it "creates a new class" do
|
||||
ClassSpecs::A.should be_kind_of(Class)
|
||||
ClassSpecs::A.new.should be_kind_of(ClassSpecs::A)
|
||||
end
|
||||
|
||||
it "has no class variables" do
|
||||
ClassSpecs::A.class_variables.should == []
|
||||
end
|
||||
|
||||
it "raises TypeError if constant given as class name exists and is not a Module" do
|
||||
lambda {
|
||||
class ClassSpecsNumber
|
||||
end
|
||||
}.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
# test case known to be detecting bugs (JRuby, MRI)
|
||||
it "raises TypeError if the constant qualifying the class is nil" do
|
||||
lambda {
|
||||
class nil::Foo
|
||||
end
|
||||
}.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises TypeError if any constant qualifying the class is not a Module" do
|
||||
lambda {
|
||||
class ClassSpecs::Number::MyClass
|
||||
end
|
||||
}.should raise_error(TypeError)
|
||||
|
||||
lambda {
|
||||
class ClassSpecsNumber::MyClass
|
||||
end
|
||||
}.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "inherits from Object by default" do
|
||||
ClassSpecs::A.superclass.should == Object
|
||||
end
|
||||
|
||||
it "raises an error when trying to change the superclass" do
|
||||
module ClassSpecs
|
||||
class SuperclassResetToSubclass < L
|
||||
end
|
||||
lambda {
|
||||
class SuperclassResetToSubclass < M
|
||||
end
|
||||
}.should raise_error(TypeError, /superclass mismatch/)
|
||||
end
|
||||
end
|
||||
|
||||
it "raises an error when reopening a class with BasicObject as superclass" do
|
||||
module ClassSpecs
|
||||
class SuperclassReopenedBasicObject < A
|
||||
end
|
||||
SuperclassReopenedBasicObject.superclass.should == A
|
||||
|
||||
lambda {
|
||||
class SuperclassReopenedBasicObject < BasicObject
|
||||
end
|
||||
}.should raise_error(TypeError, /superclass mismatch/)
|
||||
SuperclassReopenedBasicObject.superclass.should == A
|
||||
end
|
||||
end
|
||||
|
||||
# [Bug #12367] [ruby-core:75446]
|
||||
ruby_version_is "2.4" do # Until backported
|
||||
it "raises an error when reopening a class with Object as superclass" do
|
||||
module ClassSpecs
|
||||
class SuperclassReopenedObject < A
|
||||
end
|
||||
SuperclassReopenedObject.superclass.should == A
|
||||
|
||||
lambda {
|
||||
class SuperclassReopenedObject < Object
|
||||
end
|
||||
}.should raise_error(TypeError, /superclass mismatch/)
|
||||
SuperclassReopenedObject.superclass.should == A
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "allows reopening a class without specifying the superclass" do
|
||||
module ClassSpecs
|
||||
class SuperclassNotGiven < A
|
||||
end
|
||||
SuperclassNotGiven.superclass.should == A
|
||||
|
||||
class SuperclassNotGiven
|
||||
end
|
||||
SuperclassNotGiven.superclass.should == A
|
||||
end
|
||||
end
|
||||
|
||||
it "does not allow to set the superclass even if it was not specified by the first declaration" do
|
||||
module ClassSpecs
|
||||
class NoSuperclassSet
|
||||
end
|
||||
|
||||
lambda {
|
||||
class NoSuperclassSet < String
|
||||
end
|
||||
}.should raise_error(TypeError, /superclass mismatch/)
|
||||
end
|
||||
end
|
||||
|
||||
it "allows using self as the superclass if self is a class" do
|
||||
ClassSpecs::I::J.superclass.should == ClassSpecs::I
|
||||
|
||||
lambda {
|
||||
class ShouldNotWork < self; end
|
||||
}.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "first evaluates the superclass before checking if the class already exists" do
|
||||
module ClassSpecs
|
||||
class SuperclassEvaluatedFirst
|
||||
end
|
||||
a = SuperclassEvaluatedFirst
|
||||
|
||||
class SuperclassEvaluatedFirst < remove_const(:SuperclassEvaluatedFirst)
|
||||
end
|
||||
b = SuperclassEvaluatedFirst
|
||||
b.superclass.should == a
|
||||
end
|
||||
end
|
||||
|
||||
it "raises a TypeError if inheriting from a metaclass" do
|
||||
obj = mock("metaclass super")
|
||||
meta = obj.singleton_class
|
||||
lambda { class ClassSpecs::MetaclassSuper < meta; end }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "allows the declaration of class variables in the body" do
|
||||
ClassSpecs.string_class_variables(ClassSpecs::B).should == ["@@cvar"]
|
||||
ClassSpecs::B.send(:class_variable_get, :@@cvar).should == :cvar
|
||||
end
|
||||
|
||||
it "stores instance variables defined in the class body in the class object" do
|
||||
ClassSpecs.string_instance_variables(ClassSpecs::B).should include("@ivar")
|
||||
ClassSpecs::B.instance_variable_get(:@ivar).should == :ivar
|
||||
end
|
||||
|
||||
it "allows the declaration of class variables in a class method" do
|
||||
ClassSpecs::C.class_variables.should == []
|
||||
ClassSpecs::C.make_class_variable
|
||||
ClassSpecs.string_class_variables(ClassSpecs::C).should == ["@@cvar"]
|
||||
ClassSpecs::C.remove_class_variable :@@cvar
|
||||
end
|
||||
|
||||
it "allows the definition of class-level instance variables in a class method" do
|
||||
ClassSpecs.string_instance_variables(ClassSpecs::C).should_not include("@civ")
|
||||
ClassSpecs::C.make_class_instance_variable
|
||||
ClassSpecs.string_instance_variables(ClassSpecs::C).should include("@civ")
|
||||
ClassSpecs::C.remove_instance_variable :@civ
|
||||
end
|
||||
|
||||
it "allows the declaration of class variables in an instance method" do
|
||||
ClassSpecs::D.class_variables.should == []
|
||||
ClassSpecs::D.new.make_class_variable
|
||||
ClassSpecs.string_class_variables(ClassSpecs::D).should == ["@@cvar"]
|
||||
ClassSpecs::D.remove_class_variable :@@cvar
|
||||
end
|
||||
|
||||
it "allows the definition of instance methods" do
|
||||
ClassSpecs::E.new.meth.should == :meth
|
||||
end
|
||||
|
||||
it "allows the definition of class methods" do
|
||||
ClassSpecs::E.cmeth.should == :cmeth
|
||||
end
|
||||
|
||||
it "allows the definition of class methods using class << self" do
|
||||
ClassSpecs::E.smeth.should == :smeth
|
||||
end
|
||||
|
||||
it "allows the definition of Constants" do
|
||||
Object.const_defined?('CONSTANT').should == false
|
||||
ClassSpecs::E.const_defined?('CONSTANT').should == true
|
||||
ClassSpecs::E::CONSTANT.should == :constant!
|
||||
end
|
||||
|
||||
it "returns the value of the last statement in the body" do
|
||||
class ClassSpecs::Empty; end.should == nil
|
||||
class ClassSpecs::Twenty; 20; end.should == 20
|
||||
class ClassSpecs::Plus; 10 + 20; end.should == 30
|
||||
class ClassSpecs::Singleton; class << self; :singleton; end; end.should == :singleton
|
||||
end
|
||||
|
||||
describe "within a block creates a new class in the lexical scope" do
|
||||
it "for named classes at the toplevel" do
|
||||
klass = Class.new do
|
||||
class Howdy
|
||||
end
|
||||
|
||||
def self.get_class_name
|
||||
Howdy.name
|
||||
end
|
||||
end
|
||||
|
||||
Howdy.name.should == 'Howdy'
|
||||
klass.get_class_name.should == 'Howdy'
|
||||
end
|
||||
|
||||
it "for named classes in a module" do
|
||||
klass = ClassSpecs::ANON_CLASS_FOR_NEW.call
|
||||
|
||||
ClassSpecs::NamedInModule.name.should == 'ClassSpecs::NamedInModule'
|
||||
klass.get_class_name.should == 'ClassSpecs::NamedInModule'
|
||||
end
|
||||
|
||||
it "for anonymous classes" do
|
||||
klass = Class.new do
|
||||
def self.get_class
|
||||
Class.new do
|
||||
def self.foo
|
||||
'bar'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_result
|
||||
get_class.foo
|
||||
end
|
||||
end
|
||||
|
||||
klass.get_result.should == 'bar'
|
||||
end
|
||||
|
||||
it "for anonymous classes assigned to a constant" do
|
||||
klass = Class.new do
|
||||
AnonWithConstant = Class.new
|
||||
|
||||
def self.get_class_name
|
||||
AnonWithConstant.name
|
||||
end
|
||||
end
|
||||
|
||||
AnonWithConstant.name.should == 'AnonWithConstant'
|
||||
klass.get_class_name.should == 'AnonWithConstant'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "An outer class definition" do
|
||||
it "contains the inner classes" do
|
||||
ClassSpecs::Container.constants.should include(:A, :B)
|
||||
end
|
||||
end
|
||||
|
||||
describe "A class definition extending an object (sclass)" do
|
||||
it "allows adding methods" do
|
||||
ClassSpecs::O.smeth.should == :smeth
|
||||
end
|
||||
|
||||
it "raises a TypeError when trying to extend numbers" do
|
||||
lambda {
|
||||
eval <<-CODE
|
||||
class << 1
|
||||
def xyz
|
||||
self
|
||||
end
|
||||
end
|
||||
CODE
|
||||
}.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "allows accessing the block of the original scope" do
|
||||
ClassSpecs.sclass_with_block { 123 }.should == 123
|
||||
end
|
||||
|
||||
it "can use return to cause the enclosing method to return" do
|
||||
ClassSpecs.sclass_with_return.should == :inner
|
||||
end
|
||||
end
|
||||
|
||||
describe "Reopening a class" do
|
||||
it "extends the previous definitions" do
|
||||
c = ClassSpecs::F.new
|
||||
c.meth.should == :meth
|
||||
c.another.should == :another
|
||||
end
|
||||
|
||||
it "overwrites existing methods" do
|
||||
ClassSpecs::G.new.override.should == :override
|
||||
end
|
||||
|
||||
it "raises a TypeError when superclasses mismatch" do
|
||||
lambda { class ClassSpecs::A < Array; end }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "adds new methods to subclasses" do
|
||||
lambda { ClassSpecs::M.m }.should raise_error(NoMethodError)
|
||||
class ClassSpecs::L
|
||||
def self.m
|
||||
1
|
||||
end
|
||||
end
|
||||
ClassSpecs::M.m.should == 1
|
||||
ClassSpecs::L.singleton_class.send(:remove_method, :m)
|
||||
end
|
||||
end
|
||||
|
||||
describe "class provides hooks" do
|
||||
it "calls inherited when a class is created" do
|
||||
ClassSpecs::H.track_inherited.should == [ClassSpecs::K]
|
||||
end
|
||||
end
|
84
spec/ruby/language/class_variable_spec.rb
Normal file
84
spec/ruby/language/class_variable_spec.rb
Normal file
|
@ -0,0 +1,84 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/class_variables', __FILE__)
|
||||
|
||||
describe "A class variable" do
|
||||
after :each do
|
||||
ClassVariablesSpec::ClassA.new.cvar_a = :cvar_a
|
||||
end
|
||||
|
||||
it "can be accessed from a subclass" do
|
||||
ClassVariablesSpec::ClassB.new.cvar_a.should == :cvar_a
|
||||
end
|
||||
|
||||
it "is set in the superclass" do
|
||||
a = ClassVariablesSpec::ClassA.new
|
||||
b = ClassVariablesSpec::ClassB.new
|
||||
b.cvar_a = :new_val
|
||||
|
||||
a.cvar_a.should == :new_val
|
||||
end
|
||||
end
|
||||
|
||||
describe "A class variable defined in a module" do
|
||||
after :each do
|
||||
ClassVariablesSpec::ClassC.cvar_m = :value
|
||||
ClassVariablesSpec::ClassC.remove_class_variable(:@@cvar) if ClassVariablesSpec::ClassC.cvar_defined?
|
||||
end
|
||||
|
||||
it "can be accessed from classes that extend the module" do
|
||||
ClassVariablesSpec::ClassC.cvar_m.should == :value
|
||||
end
|
||||
|
||||
it "is not defined in these classes" do
|
||||
ClassVariablesSpec::ClassC.cvar_defined?.should be_false
|
||||
end
|
||||
|
||||
it "is only updated in the module a method defined in the module is used" do
|
||||
ClassVariablesSpec::ClassC.cvar_m = "new value"
|
||||
ClassVariablesSpec::ClassC.cvar_m.should == "new value"
|
||||
|
||||
ClassVariablesSpec::ClassC.cvar_defined?.should be_false
|
||||
end
|
||||
|
||||
it "is updated in the class when a Method defined in the class is used" do
|
||||
ClassVariablesSpec::ClassC.cvar_c = "new value"
|
||||
ClassVariablesSpec::ClassC.cvar_defined?.should be_true
|
||||
end
|
||||
|
||||
it "can be accessed inside the class using the module methods" do
|
||||
ClassVariablesSpec::ClassC.cvar_c = "new value"
|
||||
ClassVariablesSpec::ClassC.cvar_m.should == :value
|
||||
end
|
||||
|
||||
it "can be accessed from modules that extend the module" do
|
||||
ClassVariablesSpec::ModuleO.cvar_n.should == :value
|
||||
end
|
||||
|
||||
it "is defined in the extended module" do
|
||||
ClassVariablesSpec::ModuleN.class_variable_defined?(:@@cvar_n).should be_true
|
||||
end
|
||||
|
||||
it "is not defined in the extending module" do
|
||||
ClassVariablesSpec::ModuleO.class_variable_defined?(:@@cvar_n).should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe 'A class variable definition' do
|
||||
it "is created in a module if any of the parents do not define it" do
|
||||
a = Class.new
|
||||
b = Class.new(a)
|
||||
c = Class.new(b)
|
||||
b.class_variable_set(:@@cv, :value)
|
||||
|
||||
lambda { a.class_variable_get(:@@cv) }.should raise_error(NameError)
|
||||
b.class_variable_get(:@@cv).should == :value
|
||||
c.class_variable_get(:@@cv).should == :value
|
||||
|
||||
# updates the same variable
|
||||
c.class_variable_set(:@@cv, :next)
|
||||
|
||||
lambda { a.class_variable_get(:@@cv) }.should raise_error(NameError)
|
||||
b.class_variable_get(:@@cv).should == :next
|
||||
c.class_variable_get(:@@cv).should == :next
|
||||
end
|
||||
end
|
613
spec/ruby/language/constants_spec.rb
Normal file
613
spec/ruby/language/constants_spec.rb
Normal file
|
@ -0,0 +1,613 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/constants', __FILE__)
|
||||
require File.expand_path('../fixtures/constants_sclass', __FILE__)
|
||||
require File.expand_path('../fixtures/constant_visibility', __FILE__)
|
||||
|
||||
# Read the documentation in fixtures/constants.rb for the guidelines and
|
||||
# rationale for the structure and organization of these specs.
|
||||
|
||||
describe "Literal (A::X) constant resolution" do
|
||||
describe "with statically assigned constants" do
|
||||
it "searches the immediate class or module scope first" do
|
||||
ConstantSpecs::ClassA::CS_CONST10.should == :const10_10
|
||||
ConstantSpecs::ModuleA::CS_CONST10.should == :const10_1
|
||||
ConstantSpecs::ParentA::CS_CONST10.should == :const10_5
|
||||
ConstantSpecs::ContainerA::CS_CONST10.should == :const10_2
|
||||
ConstantSpecs::ContainerA::ChildA::CS_CONST10.should == :const10_3
|
||||
end
|
||||
|
||||
it "searches a module included in the immediate class before the superclass" do
|
||||
ConstantSpecs::ContainerA::ChildA::CS_CONST15.should == :const15_1
|
||||
end
|
||||
|
||||
it "searches the superclass before a module included in the superclass" do
|
||||
ConstantSpecs::ContainerA::ChildA::CS_CONST11.should == :const11_1
|
||||
end
|
||||
|
||||
it "searches a module included in the superclass" do
|
||||
ConstantSpecs::ContainerA::ChildA::CS_CONST12.should == :const12_1
|
||||
end
|
||||
|
||||
it "searches the superclass chain" do
|
||||
ConstantSpecs::ContainerA::ChildA::CS_CONST13.should == :const13
|
||||
end
|
||||
|
||||
it "searches Object if no class or module qualifier is given" do
|
||||
CS_CONST1.should == :const1
|
||||
CS_CONST10.should == :const10_1
|
||||
end
|
||||
|
||||
it "searches Object after searching other scopes" do
|
||||
module ConstantSpecs::SpecAdded1
|
||||
CS_CONST10.should == :const10_1
|
||||
end
|
||||
end
|
||||
|
||||
it "searches Object if a toplevel qualifier (::X) is given" do
|
||||
::CS_CONST1.should == :const1
|
||||
::CS_CONST10.should == :const10_1
|
||||
end
|
||||
|
||||
it "does not search the singleton class of the class or module" do
|
||||
lambda do
|
||||
ConstantSpecs::ContainerA::ChildA::CS_CONST14
|
||||
end.should raise_error(NameError)
|
||||
lambda { ConstantSpecs::CS_CONST14 }.should raise_error(NameError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with dynamically assigned constants" do
|
||||
it "searches the immediate class or module scope first" do
|
||||
ConstantSpecs::ClassB::CS_CONST101 = :const101_1
|
||||
ConstantSpecs::ClassB::CS_CONST101.should == :const101_1
|
||||
|
||||
ConstantSpecs::ParentB::CS_CONST101 = :const101_2
|
||||
ConstantSpecs::ParentB::CS_CONST101.should == :const101_2
|
||||
|
||||
ConstantSpecs::ContainerB::CS_CONST101 = :const101_3
|
||||
ConstantSpecs::ContainerB::CS_CONST101.should == :const101_3
|
||||
|
||||
ConstantSpecs::ContainerB::ChildB::CS_CONST101 = :const101_4
|
||||
ConstantSpecs::ContainerB::ChildB::CS_CONST101.should == :const101_4
|
||||
|
||||
ConstantSpecs::ModuleA::CS_CONST101 = :const101_5
|
||||
ConstantSpecs::ModuleA::CS_CONST101.should == :const101_5
|
||||
end
|
||||
|
||||
it "searches a module included in the immediate class before the superclass" do
|
||||
ConstantSpecs::ParentB::CS_CONST102 = :const102_1
|
||||
ConstantSpecs::ModuleF::CS_CONST102 = :const102_2
|
||||
ConstantSpecs::ContainerB::ChildB::CS_CONST102.should == :const102_2
|
||||
end
|
||||
|
||||
it "searches the superclass before a module included in the superclass" do
|
||||
ConstantSpecs::ModuleE::CS_CONST103 = :const103_1
|
||||
ConstantSpecs::ParentB::CS_CONST103 = :const103_2
|
||||
ConstantSpecs::ContainerB::ChildB::CS_CONST103.should == :const103_2
|
||||
end
|
||||
|
||||
it "searches a module included in the superclass" do
|
||||
ConstantSpecs::ModuleA::CS_CONST104 = :const104_1
|
||||
ConstantSpecs::ModuleE::CS_CONST104 = :const104_2
|
||||
ConstantSpecs::ContainerB::ChildB::CS_CONST104.should == :const104_2
|
||||
end
|
||||
|
||||
it "searches the superclass chain" do
|
||||
ConstantSpecs::ModuleA::CS_CONST105 = :const105
|
||||
ConstantSpecs::ContainerB::ChildB::CS_CONST105.should == :const105
|
||||
end
|
||||
|
||||
it "searches Object if no class or module qualifier is given" do
|
||||
CS_CONST106 = :const106
|
||||
CS_CONST106.should == :const106
|
||||
end
|
||||
|
||||
it "searches Object if a toplevel qualifier (::X) is given" do
|
||||
::CS_CONST107 = :const107
|
||||
::CS_CONST107.should == :const107
|
||||
end
|
||||
|
||||
it "does not search the singleton class of the class or module" do
|
||||
class << ConstantSpecs::ContainerB::ChildB
|
||||
CS_CONST108 = :const108_1
|
||||
end
|
||||
|
||||
lambda do
|
||||
ConstantSpecs::ContainerB::ChildB::CS_CONST108
|
||||
end.should raise_error(NameError)
|
||||
|
||||
module ConstantSpecs
|
||||
class << self
|
||||
CS_CONST108 = :const108_2
|
||||
end
|
||||
end
|
||||
|
||||
lambda { ConstantSpecs::CS_CONST108 }.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "returns the updated value when a constant is reassigned" do
|
||||
ConstantSpecs::ClassB::CS_CONST109 = :const109_1
|
||||
ConstantSpecs::ClassB::CS_CONST109.should == :const109_1
|
||||
|
||||
-> {
|
||||
ConstantSpecs::ClassB::CS_CONST109 = :const109_2
|
||||
}.should complain(/already initialized constant/)
|
||||
ConstantSpecs::ClassB::CS_CONST109.should == :const109_2
|
||||
end
|
||||
|
||||
it "evaluates the right hand side before evaluating a constant path" do
|
||||
mod = Module.new
|
||||
|
||||
mod.module_eval <<-EOC
|
||||
ConstantSpecsRHS::B = begin
|
||||
module ConstantSpecsRHS; end
|
||||
|
||||
"hello"
|
||||
end
|
||||
EOC
|
||||
|
||||
mod::ConstantSpecsRHS::B.should == 'hello'
|
||||
end
|
||||
end
|
||||
|
||||
it "raises a NameError if no constant is defined in the search path" do
|
||||
lambda { ConstantSpecs::ParentA::CS_CONSTX }.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "sends #const_missing to the original class or module scope" do
|
||||
ConstantSpecs::ClassA::CS_CONSTX.should == :CS_CONSTX
|
||||
end
|
||||
|
||||
it "evaluates the qualifier" do
|
||||
ConstantSpecs.get_const::CS_CONST2.should == :const2
|
||||
end
|
||||
|
||||
it "raises a TypeError if a non-class or non-module qualifier is given" do
|
||||
lambda { CS_CONST1::CS_CONST }.should raise_error(TypeError)
|
||||
lambda { 1::CS_CONST }.should raise_error(TypeError)
|
||||
lambda { "mod"::CS_CONST }.should raise_error(TypeError)
|
||||
lambda { false::CS_CONST }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Constant resolution within methods" do
|
||||
describe "with statically assigned constants" do
|
||||
it "searches the immediate class or module scope first" do
|
||||
ConstantSpecs::ClassA.const10.should == :const10_10
|
||||
ConstantSpecs::ParentA.const10.should == :const10_5
|
||||
ConstantSpecs::ContainerA.const10.should == :const10_2
|
||||
ConstantSpecs::ContainerA::ChildA.const10.should == :const10_3
|
||||
|
||||
ConstantSpecs::ClassA.new.const10.should == :const10_10
|
||||
ConstantSpecs::ParentA.new.const10.should == :const10_5
|
||||
ConstantSpecs::ContainerA::ChildA.new.const10.should == :const10_3
|
||||
end
|
||||
|
||||
it "searches a module included in the immediate class before the superclass" do
|
||||
ConstantSpecs::ContainerA::ChildA.const15.should == :const15_1
|
||||
ConstantSpecs::ContainerA::ChildA.new.const15.should == :const15_1
|
||||
end
|
||||
|
||||
it "searches the superclass before a module included in the superclass" do
|
||||
ConstantSpecs::ContainerA::ChildA.const11.should == :const11_1
|
||||
ConstantSpecs::ContainerA::ChildA.new.const11.should == :const11_1
|
||||
end
|
||||
|
||||
it "searches a module included in the superclass" do
|
||||
ConstantSpecs::ContainerA::ChildA.const12.should == :const12_1
|
||||
ConstantSpecs::ContainerA::ChildA.new.const12.should == :const12_1
|
||||
end
|
||||
|
||||
it "searches the superclass chain" do
|
||||
ConstantSpecs::ContainerA::ChildA.const13.should == :const13
|
||||
ConstantSpecs::ContainerA::ChildA.new.const13.should == :const13
|
||||
end
|
||||
|
||||
it "searches the lexical scope of the method not the receiver's immediate class" do
|
||||
ConstantSpecs::ContainerA::ChildA.const19.should == :const19_1
|
||||
end
|
||||
|
||||
it "searches the lexical scope of a singleton method" do
|
||||
ConstantSpecs::CS_CONST18.const17.should == :const17_1
|
||||
end
|
||||
|
||||
it "does not search the lexical scope of the caller" do
|
||||
lambda { ConstantSpecs::ClassA.const16 }.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "searches the lexical scope of a block" do
|
||||
ConstantSpecs::ClassA.const22.should == :const22_1
|
||||
end
|
||||
|
||||
it "searches Object as a lexical scope only if Object is explicitly opened" do
|
||||
ConstantSpecs::ContainerA::ChildA.const20.should == :const20_1
|
||||
ConstantSpecs::ContainerA::ChildA.const21.should == :const21_1
|
||||
end
|
||||
|
||||
it "does not search the lexical scope of qualifying modules" do
|
||||
lambda do
|
||||
ConstantSpecs::ContainerA::ChildA.const23
|
||||
end.should raise_error(NameError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with dynamically assigned constants" do
|
||||
it "searches the immediate class or module scope first" do
|
||||
ConstantSpecs::ModuleA::CS_CONST201 = :const201_1
|
||||
|
||||
class ConstantSpecs::ClassB; CS_CONST201 = :const201_2; end
|
||||
ConstantSpecs::ParentB::CS_CONST201 = :const201_3
|
||||
ConstantSpecs::ContainerB::CS_CONST201 = :const201_4
|
||||
ConstantSpecs::ContainerB::ChildB::CS_CONST201 = :const201_5
|
||||
|
||||
ConstantSpecs::ClassB.const201.should == :const201_2
|
||||
ConstantSpecs::ParentB.const201.should == :const201_3
|
||||
ConstantSpecs::ContainerB.const201.should == :const201_4
|
||||
ConstantSpecs::ContainerB::ChildB.const201.should == :const201_5
|
||||
|
||||
ConstantSpecs::ClassB.new.const201.should == :const201_2
|
||||
ConstantSpecs::ParentB.new.const201.should == :const201_3
|
||||
ConstantSpecs::ContainerB::ChildB.new.const201.should == :const201_5
|
||||
end
|
||||
|
||||
it "searches a module included in the immediate class before the superclass" do
|
||||
ConstantSpecs::ParentB::CS_CONST202 = :const202_2
|
||||
ConstantSpecs::ContainerB::ChildB::CS_CONST202 = :const202_1
|
||||
|
||||
ConstantSpecs::ContainerB::ChildB.const202.should == :const202_1
|
||||
ConstantSpecs::ContainerB::ChildB.new.const202.should == :const202_1
|
||||
end
|
||||
|
||||
it "searches the superclass before a module included in the superclass" do
|
||||
ConstantSpecs::ParentB::CS_CONST203 = :const203_1
|
||||
ConstantSpecs::ModuleE::CS_CONST203 = :const203_2
|
||||
|
||||
ConstantSpecs::ContainerB::ChildB.const203.should == :const203_1
|
||||
ConstantSpecs::ContainerB::ChildB.new.const203.should == :const203_1
|
||||
end
|
||||
|
||||
it "searches a module included in the superclass" do
|
||||
ConstantSpecs::ModuleA::CS_CONST204 = :const204_2
|
||||
ConstantSpecs::ModuleE::CS_CONST204 = :const204_1
|
||||
|
||||
ConstantSpecs::ContainerB::ChildB.const204.should == :const204_1
|
||||
ConstantSpecs::ContainerB::ChildB.new.const204.should == :const204_1
|
||||
end
|
||||
|
||||
it "searches the superclass chain" do
|
||||
ConstantSpecs::ModuleA::CS_CONST205 = :const205
|
||||
|
||||
ConstantSpecs::ContainerB::ChildB.const205.should == :const205
|
||||
ConstantSpecs::ContainerB::ChildB.new.const205.should == :const205
|
||||
end
|
||||
|
||||
it "searches the lexical scope of the method not the receiver's immediate class" do
|
||||
ConstantSpecs::ContainerB::ChildB::CS_CONST206 = :const206_2
|
||||
class ConstantSpecs::ContainerB::ChildB
|
||||
class << self
|
||||
CS_CONST206 = :const206_1
|
||||
end
|
||||
end
|
||||
|
||||
ConstantSpecs::ContainerB::ChildB.const206.should == :const206_1
|
||||
end
|
||||
|
||||
it "searches the lexical scope of a singleton method" do
|
||||
ConstantSpecs::CS_CONST207 = :const207_1
|
||||
ConstantSpecs::ClassB::CS_CONST207 = :const207_2
|
||||
|
||||
ConstantSpecs::CS_CONST208.const207.should == :const207_1
|
||||
end
|
||||
|
||||
it "does not search the lexical scope of the caller" do
|
||||
ConstantSpecs::ClassB::CS_CONST209 = :const209
|
||||
|
||||
lambda { ConstantSpecs::ClassB.const209 }.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "searches the lexical scope of a block" do
|
||||
ConstantSpecs::ClassB::CS_CONST210 = :const210_1
|
||||
ConstantSpecs::ParentB::CS_CONST210 = :const210_2
|
||||
|
||||
ConstantSpecs::ClassB.const210.should == :const210_1
|
||||
end
|
||||
|
||||
it "searches Object as a lexical scope only if Object is explicitly opened" do
|
||||
Object::CS_CONST211 = :const211_1
|
||||
ConstantSpecs::ParentB::CS_CONST211 = :const211_2
|
||||
ConstantSpecs::ContainerB::ChildB.const211.should == :const211_1
|
||||
|
||||
Object::CS_CONST212 = :const212_2
|
||||
ConstantSpecs::ParentB::CS_CONST212 = :const212_1
|
||||
ConstantSpecs::ContainerB::ChildB.const212.should == :const212_1
|
||||
end
|
||||
|
||||
it "returns the updated value when a constant is reassigned" do
|
||||
ConstantSpecs::ParentB::CS_CONST213 = :const213_1
|
||||
ConstantSpecs::ContainerB::ChildB.const213.should == :const213_1
|
||||
ConstantSpecs::ContainerB::ChildB.new.const213.should == :const213_1
|
||||
|
||||
-> {
|
||||
ConstantSpecs::ParentB::CS_CONST213 = :const213_2
|
||||
}.should complain(/already initialized constant/)
|
||||
ConstantSpecs::ContainerB::ChildB.const213.should == :const213_2
|
||||
ConstantSpecs::ContainerB::ChildB.new.const213.should == :const213_2
|
||||
end
|
||||
|
||||
it "does not search the lexical scope of qualifying modules" do
|
||||
ConstantSpecs::ContainerB::CS_CONST214 = :const214
|
||||
|
||||
lambda do
|
||||
ConstantSpecs::ContainerB::ChildB.const214
|
||||
end.should raise_error(NameError)
|
||||
end
|
||||
end
|
||||
|
||||
it "raises a NameError if no constant is defined in the search path" do
|
||||
lambda { ConstantSpecs::ParentA.constx }.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "sends #const_missing to the original class or module scope" do
|
||||
ConstantSpecs::ClassA.constx.should == :CS_CONSTX
|
||||
ConstantSpecs::ClassA.new.constx.should == :CS_CONSTX
|
||||
end
|
||||
|
||||
describe "with ||=" do
|
||||
it "assignes constant if previously undefined" do
|
||||
ConstantSpecs.should_not have_constant(:OpAssignUndefined)
|
||||
# Literally opening the module is required to avoid content
|
||||
# re-assignment error
|
||||
module ConstantSpecs
|
||||
OpAssignUndefined ||= 42
|
||||
end
|
||||
ConstantSpecs::OpAssignUndefined.should == 42
|
||||
ConstantSpecs.send(:remove_const, :OpAssignUndefined)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Constant resolution within a singleton class (class << obj)" do
|
||||
it "works like normal classes or modules" do
|
||||
ConstantSpecs::CS_SINGLETON1.foo.should == 1
|
||||
end
|
||||
|
||||
ruby_version_is "2.3" do
|
||||
it "uses its own namespace for each object" do
|
||||
a = ConstantSpecs::CS_SINGLETON2[0].foo
|
||||
b = ConstantSpecs::CS_SINGLETON2[1].foo
|
||||
[a, b].should == [1, 2]
|
||||
end
|
||||
|
||||
it "uses its own namespace for nested modules" do
|
||||
a = ConstantSpecs::CS_SINGLETON3[0].x
|
||||
b = ConstantSpecs::CS_SINGLETON3[1].x
|
||||
a.should_not equal(b)
|
||||
end
|
||||
|
||||
it "allows nested modules to have proper resolution" do
|
||||
a = ConstantSpecs::CS_SINGLETON4_CLASSES[0].new
|
||||
b = ConstantSpecs::CS_SINGLETON4_CLASSES[1].new
|
||||
[a.foo, b.foo].should == [1, 2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Module#private_constant marked constants" do
|
||||
|
||||
it "remain private even when updated" do
|
||||
mod = Module.new
|
||||
mod.const_set :Foo, true
|
||||
mod.send :private_constant, :Foo
|
||||
-> {
|
||||
mod.const_set :Foo, false
|
||||
}.should complain(/already initialized constant/)
|
||||
|
||||
lambda {mod::Foo}.should raise_error(NameError)
|
||||
end
|
||||
|
||||
describe "in a module" do
|
||||
it "cannot be accessed from outside the module" do
|
||||
lambda do
|
||||
ConstantVisibility::PrivConstModule::PRIVATE_CONSTANT_MODULE
|
||||
end.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "cannot be reopened as a module from scope where constant would be private" do
|
||||
lambda do
|
||||
module ConstantVisibility::ModuleContainer::PrivateModule; end
|
||||
end.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "cannot be reopened as a class from scope where constant would be private" do
|
||||
lambda do
|
||||
class ConstantVisibility::ModuleContainer::PrivateClass; end
|
||||
end.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "can be reopened as a module where constant is not private" do
|
||||
module ::ConstantVisibility::ModuleContainer
|
||||
module PrivateModule
|
||||
X = 1
|
||||
end
|
||||
|
||||
PrivateModule::X.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
it "can be reopened as a class where constant is not private" do
|
||||
module ::ConstantVisibility::ModuleContainer
|
||||
class PrivateClass
|
||||
X = 1
|
||||
end
|
||||
|
||||
PrivateClass::X.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
it "is not defined? with A::B form" do
|
||||
defined?(ConstantVisibility::PrivConstModule::PRIVATE_CONSTANT_MODULE).should == nil
|
||||
end
|
||||
|
||||
it "can be accessed from the module itself" do
|
||||
ConstantVisibility::PrivConstModule.private_constant_from_self.should be_true
|
||||
end
|
||||
|
||||
it "is defined? from the module itself" do
|
||||
ConstantVisibility::PrivConstModule.defined_from_self.should == "constant"
|
||||
end
|
||||
|
||||
it "can be accessed from lexical scope" do
|
||||
ConstantVisibility::PrivConstModule::Nested.private_constant_from_scope.should be_true
|
||||
end
|
||||
|
||||
it "is defined? from lexical scope" do
|
||||
ConstantVisibility::PrivConstModule::Nested.defined_from_scope.should == "constant"
|
||||
end
|
||||
|
||||
it "can be accessed from classes that include the module" do
|
||||
ConstantVisibility::PrivConstModuleChild.new.private_constant_from_include.should be_true
|
||||
end
|
||||
|
||||
it "is defined? from classes that include the module" do
|
||||
ConstantVisibility::PrivConstModuleChild.new.defined_from_include.should == "constant"
|
||||
end
|
||||
end
|
||||
|
||||
describe "in a class" do
|
||||
it "cannot be accessed from outside the class" do
|
||||
lambda do
|
||||
ConstantVisibility::PrivConstClass::PRIVATE_CONSTANT_CLASS
|
||||
end.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "cannot be reopened as a module" do
|
||||
lambda do
|
||||
module ConstantVisibility::ClassContainer::PrivateModule; end
|
||||
end.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "cannot be reopened as a class" do
|
||||
lambda do
|
||||
class ConstantVisibility::ClassContainer::PrivateClass; end
|
||||
end.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "can be reopened as a module where constant is not private" do
|
||||
class ::ConstantVisibility::ClassContainer
|
||||
module PrivateModule
|
||||
X = 1
|
||||
end
|
||||
|
||||
PrivateModule::X.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
it "can be reopened as a class where constant is not private" do
|
||||
class ::ConstantVisibility::ClassContainer
|
||||
class PrivateClass
|
||||
X = 1
|
||||
end
|
||||
|
||||
PrivateClass::X.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
it "is not defined? with A::B form" do
|
||||
defined?(ConstantVisibility::PrivConstClass::PRIVATE_CONSTANT_CLASS).should == nil
|
||||
end
|
||||
|
||||
it "can be accessed from the class itself" do
|
||||
ConstantVisibility::PrivConstClass.private_constant_from_self.should be_true
|
||||
end
|
||||
|
||||
it "is defined? from the class itself" do
|
||||
ConstantVisibility::PrivConstClass.defined_from_self.should == "constant"
|
||||
end
|
||||
|
||||
it "can be accessed from lexical scope" do
|
||||
ConstantVisibility::PrivConstClass::Nested.private_constant_from_scope.should be_true
|
||||
end
|
||||
|
||||
it "is defined? from lexical scope" do
|
||||
ConstantVisibility::PrivConstClass::Nested.defined_from_scope.should == "constant"
|
||||
end
|
||||
|
||||
it "can be accessed from subclasses" do
|
||||
ConstantVisibility::PrivConstClassChild.new.private_constant_from_subclass.should be_true
|
||||
end
|
||||
|
||||
it "is defined? from subclasses" do
|
||||
ConstantVisibility::PrivConstClassChild.new.defined_from_subclass.should == "constant"
|
||||
end
|
||||
end
|
||||
|
||||
describe "in Object" do
|
||||
it "cannot be accessed using ::Const form" do
|
||||
lambda do
|
||||
::PRIVATE_CONSTANT_IN_OBJECT
|
||||
end.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "is not defined? using ::Const form" do
|
||||
defined?(::PRIVATE_CONSTANT_IN_OBJECT).should == nil
|
||||
end
|
||||
|
||||
it "can be accessed through the normal search" do
|
||||
PRIVATE_CONSTANT_IN_OBJECT.should == true
|
||||
end
|
||||
|
||||
it "is defined? through the normal search" do
|
||||
defined?(PRIVATE_CONSTANT_IN_OBJECT).should == "constant"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Module#public_constant marked constants" do
|
||||
before :each do
|
||||
@module = ConstantVisibility::PrivConstModule.dup
|
||||
end
|
||||
|
||||
describe "in a module" do
|
||||
it "can be accessed from outside the module" do
|
||||
@module.send :public_constant, :PRIVATE_CONSTANT_MODULE
|
||||
@module::PRIVATE_CONSTANT_MODULE.should == true
|
||||
end
|
||||
|
||||
it "is defined? with A::B form" do
|
||||
@module.send :public_constant, :PRIVATE_CONSTANT_MODULE
|
||||
defined?(@module::PRIVATE_CONSTANT_MODULE).should == "constant"
|
||||
end
|
||||
end
|
||||
|
||||
describe "in a class" do
|
||||
before :each do
|
||||
@class = ConstantVisibility::PrivConstClass.dup
|
||||
end
|
||||
|
||||
it "can be accessed from outside the class" do
|
||||
@class.send :public_constant, :PRIVATE_CONSTANT_CLASS
|
||||
@class::PRIVATE_CONSTANT_CLASS.should == true
|
||||
end
|
||||
|
||||
it "is defined? with A::B form" do
|
||||
@class.send :public_constant, :PRIVATE_CONSTANT_CLASS
|
||||
defined?(@class::PRIVATE_CONSTANT_CLASS).should == "constant"
|
||||
end
|
||||
end
|
||||
|
||||
describe "in Object" do
|
||||
after :each do
|
||||
ConstantVisibility.reset_private_constants
|
||||
end
|
||||
|
||||
it "can be accessed using ::Const form" do
|
||||
Object.send :public_constant, :PRIVATE_CONSTANT_IN_OBJECT
|
||||
::PRIVATE_CONSTANT_IN_OBJECT.should == true
|
||||
end
|
||||
|
||||
it "is defined? using ::Const form" do
|
||||
Object.send :public_constant, :PRIVATE_CONSTANT_IN_OBJECT
|
||||
defined?(::PRIVATE_CONSTANT_IN_OBJECT).should == "constant"
|
||||
end
|
||||
end
|
||||
end
|
714
spec/ruby/language/def_spec.rb
Normal file
714
spec/ruby/language/def_spec.rb
Normal file
|
@ -0,0 +1,714 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/def', __FILE__)
|
||||
|
||||
# Language-level method behaviour
|
||||
describe "Redefining a method" do
|
||||
it "replaces the original method" do
|
||||
def barfoo; 100; end
|
||||
barfoo.should == 100
|
||||
|
||||
def barfoo; 200; end
|
||||
barfoo.should == 200
|
||||
end
|
||||
end
|
||||
|
||||
describe "Defining a method at the top-level" do
|
||||
it "defines it on Object with private visibility by default" do
|
||||
Object.should have_private_instance_method(:some_toplevel_method, false)
|
||||
end
|
||||
|
||||
it "defines it on Object with public visibility after calling public" do
|
||||
Object.should have_public_instance_method(:public_toplevel_method, false)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Defining an 'initialize' method" do
|
||||
it "sets the method's visibility to private" do
|
||||
class DefInitializeSpec
|
||||
def initialize
|
||||
end
|
||||
end
|
||||
DefInitializeSpec.should have_private_instance_method(:initialize, false)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Defining an 'initialize_copy' method" do
|
||||
it "sets the method's visibility to private" do
|
||||
class DefInitializeCopySpec
|
||||
def initialize_copy
|
||||
end
|
||||
end
|
||||
DefInitializeCopySpec.should have_private_instance_method(:initialize_copy, false)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Defining an 'initialize_dup' method" do
|
||||
it "sets the method's visibility to private" do
|
||||
class DefInitializeDupSpec
|
||||
def initialize_dup
|
||||
end
|
||||
end
|
||||
DefInitializeDupSpec.should have_private_instance_method(:initialize_dup, false)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Defining an 'initialize_clone' method" do
|
||||
it "sets the method's visibility to private" do
|
||||
class DefInitializeCloneSpec
|
||||
def initialize_clone
|
||||
end
|
||||
end
|
||||
DefInitializeCloneSpec.should have_private_instance_method(:initialize_clone, false)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Defining a 'respond_to_missing?' method" do
|
||||
it "sets the method's visibility to private" do
|
||||
class DefRespondToMissingPSpec
|
||||
def respond_to_missing?
|
||||
end
|
||||
end
|
||||
DefRespondToMissingPSpec.should have_private_instance_method(:respond_to_missing?, false)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Defining a method" do
|
||||
it "returns a symbol of the method name" do
|
||||
method_name = def some_method; end
|
||||
method_name.should == :some_method
|
||||
end
|
||||
end
|
||||
|
||||
describe "An instance method definition with a splat" do
|
||||
it "accepts an unnamed '*' argument" do
|
||||
def foo(*); end;
|
||||
|
||||
foo.should == nil
|
||||
foo(1, 2).should == nil
|
||||
foo(1, 2, 3, 4, :a, :b, 'c', 'd').should == nil
|
||||
end
|
||||
|
||||
it "accepts a named * argument" do
|
||||
def foo(*a); a; end;
|
||||
foo.should == []
|
||||
foo(1, 2).should == [1, 2]
|
||||
foo([:a]).should == [[:a]]
|
||||
end
|
||||
|
||||
it "accepts non-* arguments before the * argument" do
|
||||
def foo(a, b, c, d, e, *f); [a, b, c, d, e, f]; end
|
||||
foo(1, 2, 3, 4, 5, 6, 7, 8).should == [1, 2, 3, 4, 5, [6, 7, 8]]
|
||||
end
|
||||
|
||||
it "allows only a single * argument" do
|
||||
lambda { eval 'def foo(a, *b, *c); end' }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "requires the presence of any arguments that precede the *" do
|
||||
def foo(a, b, *c); end
|
||||
lambda { foo 1 }.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "An instance method with a default argument" do
|
||||
it "evaluates the default when no arguments are passed" do
|
||||
def foo(a = 1)
|
||||
a
|
||||
end
|
||||
foo.should == 1
|
||||
foo(2).should == 2
|
||||
end
|
||||
|
||||
it "evaluates the default empty expression when no arguments are passed" do
|
||||
def foo(a = ())
|
||||
a
|
||||
end
|
||||
foo.should == nil
|
||||
foo(2).should == 2
|
||||
end
|
||||
|
||||
it "assigns an empty Array to an unused splat argument" do
|
||||
def foo(a = 1, *b)
|
||||
[a,b]
|
||||
end
|
||||
foo.should == [1, []]
|
||||
foo(2).should == [2, []]
|
||||
end
|
||||
|
||||
it "evaluates the default when required arguments precede it" do
|
||||
def foo(a, b = 2)
|
||||
[a,b]
|
||||
end
|
||||
lambda { foo }.should raise_error(ArgumentError)
|
||||
foo(1).should == [1, 2]
|
||||
end
|
||||
|
||||
it "prefers to assign to a default argument before a splat argument" do
|
||||
def foo(a, b = 2, *c)
|
||||
[a,b,c]
|
||||
end
|
||||
lambda { foo }.should raise_error(ArgumentError)
|
||||
foo(1).should == [1,2,[]]
|
||||
end
|
||||
|
||||
it "prefers to assign to a default argument when there are no required arguments" do
|
||||
def foo(a = 1, *args)
|
||||
[a,args]
|
||||
end
|
||||
foo(2,2).should == [2,[2]]
|
||||
end
|
||||
|
||||
it "does not evaluate the default when passed a value and a * argument" do
|
||||
def foo(a, b = 2, *args)
|
||||
[a,b,args]
|
||||
end
|
||||
foo(2,3,3).should == [2,3,[3]]
|
||||
end
|
||||
|
||||
it "shadows an existing method with the same name as the local" do
|
||||
def bar
|
||||
1
|
||||
end
|
||||
-> {
|
||||
eval "def foo(bar = bar)
|
||||
bar
|
||||
end"
|
||||
}.should complain(/circular argument reference/)
|
||||
foo.should == nil
|
||||
foo(2).should == 2
|
||||
end
|
||||
|
||||
it "calls a method with the same name as the local when explicitly using ()" do
|
||||
def bar
|
||||
1
|
||||
end
|
||||
def foo(bar = bar())
|
||||
bar
|
||||
end
|
||||
foo.should == 1
|
||||
foo(2).should == 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "A singleton method definition" do
|
||||
it "can be declared for a local variable" do
|
||||
a = Object.new
|
||||
def a.foo
|
||||
5
|
||||
end
|
||||
a.foo.should == 5
|
||||
end
|
||||
|
||||
it "can be declared for an instance variable" do
|
||||
@a = Object.new
|
||||
def @a.foo
|
||||
6
|
||||
end
|
||||
@a.foo.should == 6
|
||||
end
|
||||
|
||||
it "can be declared for a global variable" do
|
||||
$__a__ = "hi"
|
||||
def $__a__.foo
|
||||
7
|
||||
end
|
||||
$__a__.foo.should == 7
|
||||
end
|
||||
|
||||
it "can be declared with an empty method body" do
|
||||
class DefSpec
|
||||
def self.foo;end
|
||||
end
|
||||
DefSpec.foo.should == nil
|
||||
end
|
||||
|
||||
it "can be redefined" do
|
||||
obj = Object.new
|
||||
def obj.==(other)
|
||||
1
|
||||
end
|
||||
(obj==1).should == 1
|
||||
def obj.==(other)
|
||||
2
|
||||
end
|
||||
(obj==2).should == 2
|
||||
end
|
||||
|
||||
it "raises RuntimeError if frozen" do
|
||||
obj = Object.new
|
||||
obj.freeze
|
||||
lambda { def obj.foo; end }.should raise_error(RuntimeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Redefining a singleton method" do
|
||||
it "does not inherit a previously set visibility" do
|
||||
o = Object.new
|
||||
|
||||
class << o; private; def foo; end; end;
|
||||
|
||||
class << o; should have_private_instance_method(:foo); end
|
||||
|
||||
class << o; def foo; end; end;
|
||||
|
||||
class << o; should_not have_private_instance_method(:foo); end
|
||||
class << o; should have_instance_method(:foo); end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
describe "Redefining a singleton method" do
|
||||
it "does not inherit a previously set visibility" do
|
||||
o = Object.new
|
||||
|
||||
class << o; private; def foo; end; end;
|
||||
|
||||
class << o; should have_private_instance_method(:foo); end
|
||||
|
||||
class << o; def foo; end; end;
|
||||
|
||||
class << o; should_not have_private_instance_method(:foo); end
|
||||
class << o; should have_instance_method(:foo); end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
describe "A method defined with extreme default arguments" do
|
||||
it "can redefine itself when the default is evaluated" do
|
||||
class DefSpecs
|
||||
def foo(x = (def foo; "hello"; end;1));x;end
|
||||
end
|
||||
|
||||
d = DefSpecs.new
|
||||
d.foo(42).should == 42
|
||||
d.foo.should == 1
|
||||
d.foo.should == 'hello'
|
||||
end
|
||||
|
||||
it "may use an fcall as a default" do
|
||||
def bar
|
||||
1
|
||||
end
|
||||
def foo(x = bar())
|
||||
x
|
||||
end
|
||||
foo.should == 1
|
||||
foo(2).should == 2
|
||||
end
|
||||
|
||||
it "evaluates the defaults in the method's scope" do
|
||||
def foo(x = ($foo_self = self; nil)); end
|
||||
foo
|
||||
$foo_self.should == self
|
||||
end
|
||||
|
||||
it "may use preceding arguments as defaults" do
|
||||
def foo(obj, width=obj.length)
|
||||
width
|
||||
end
|
||||
foo('abcde').should == 5
|
||||
end
|
||||
|
||||
it "may use a lambda as a default" do
|
||||
def foo(output = 'a', prc = lambda {|n| output * n})
|
||||
prc.call(5)
|
||||
end
|
||||
foo.should == 'aaaaa'
|
||||
end
|
||||
end
|
||||
|
||||
describe "A singleton method defined with extreme default arguments" do
|
||||
it "may use a method definition as a default" do
|
||||
$__a = Object.new
|
||||
def $__a.foo(x = (def $__a.foo; "hello"; end;1));x;end
|
||||
|
||||
$__a.foo(42).should == 42
|
||||
$__a.foo.should == 1
|
||||
$__a.foo.should == 'hello'
|
||||
end
|
||||
|
||||
it "may use an fcall as a default" do
|
||||
a = Object.new
|
||||
def a.bar
|
||||
1
|
||||
end
|
||||
def a.foo(x = bar())
|
||||
x
|
||||
end
|
||||
a.foo.should == 1
|
||||
a.foo(2).should == 2
|
||||
end
|
||||
|
||||
it "evaluates the defaults in the singleton scope" do
|
||||
a = Object.new
|
||||
def a.foo(x = ($foo_self = self; nil)); 5 ;end
|
||||
a.foo
|
||||
$foo_self.should == a
|
||||
end
|
||||
|
||||
it "may use preceding arguments as defaults" do
|
||||
a = Object.new
|
||||
def a.foo(obj, width=obj.length)
|
||||
width
|
||||
end
|
||||
a.foo('abcde').should == 5
|
||||
end
|
||||
|
||||
it "may use a lambda as a default" do
|
||||
a = Object.new
|
||||
def a.foo(output = 'a', prc = lambda {|n| output * n})
|
||||
prc.call(5)
|
||||
end
|
||||
a.foo.should == 'aaaaa'
|
||||
end
|
||||
end
|
||||
|
||||
describe "A method definition inside a metaclass scope" do
|
||||
it "can create a class method" do
|
||||
class DefSpecSingleton
|
||||
class << self
|
||||
def a_class_method;self;end
|
||||
end
|
||||
end
|
||||
|
||||
DefSpecSingleton.a_class_method.should == DefSpecSingleton
|
||||
lambda { Object.a_class_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "can create a singleton method" do
|
||||
obj = Object.new
|
||||
class << obj
|
||||
def a_singleton_method;self;end
|
||||
end
|
||||
|
||||
obj.a_singleton_method.should == obj
|
||||
lambda { Object.new.a_singleton_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "raises RuntimeError if frozen" do
|
||||
obj = Object.new
|
||||
obj.freeze
|
||||
|
||||
class << obj
|
||||
lambda { def foo; end }.should raise_error(RuntimeError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "A nested method definition" do
|
||||
it "creates an instance method when evaluated in an instance method" do
|
||||
class DefSpecNested
|
||||
def create_instance_method
|
||||
def an_instance_method;self;end
|
||||
an_instance_method
|
||||
end
|
||||
end
|
||||
|
||||
obj = DefSpecNested.new
|
||||
obj.create_instance_method.should == obj
|
||||
obj.an_instance_method.should == obj
|
||||
|
||||
other = DefSpecNested.new
|
||||
other.an_instance_method.should == other
|
||||
|
||||
DefSpecNested.should have_instance_method(:an_instance_method)
|
||||
end
|
||||
|
||||
it "creates a class method when evaluated in a class method" do
|
||||
class DefSpecNested
|
||||
class << self
|
||||
# cleanup
|
||||
remove_method :a_class_method if method_defined? :a_class_method
|
||||
def create_class_method
|
||||
def a_class_method;self;end
|
||||
a_class_method
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
lambda { DefSpecNested.a_class_method }.should raise_error(NoMethodError)
|
||||
DefSpecNested.create_class_method.should == DefSpecNested
|
||||
DefSpecNested.a_class_method.should == DefSpecNested
|
||||
lambda { Object.a_class_method }.should raise_error(NoMethodError)
|
||||
lambda { DefSpecNested.new.a_class_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "creates a singleton method when evaluated in the metaclass of an instance" do
|
||||
class DefSpecNested
|
||||
def create_singleton_method
|
||||
class << self
|
||||
def a_singleton_method;self;end
|
||||
end
|
||||
a_singleton_method
|
||||
end
|
||||
end
|
||||
|
||||
obj = DefSpecNested.new
|
||||
obj.create_singleton_method.should == obj
|
||||
obj.a_singleton_method.should == obj
|
||||
|
||||
other = DefSpecNested.new
|
||||
lambda { other.a_singleton_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "creates a method in the surrounding context when evaluated in a def expr.method" do
|
||||
class DefSpecNested
|
||||
TARGET = Object.new
|
||||
def TARGET.defs_method
|
||||
def inherited_method;self;end
|
||||
end
|
||||
end
|
||||
|
||||
DefSpecNested::TARGET.defs_method
|
||||
DefSpecNested.should have_instance_method :inherited_method
|
||||
DefSpecNested::TARGET.should_not have_method :inherited_method
|
||||
|
||||
obj = DefSpecNested.new
|
||||
obj.inherited_method.should == obj
|
||||
end
|
||||
|
||||
# See http://yugui.jp/articles/846#label-3
|
||||
it "inside an instance_eval creates a singleton method" do
|
||||
class DefSpecNested
|
||||
OBJ = Object.new
|
||||
OBJ.instance_eval do
|
||||
def create_method_in_instance_eval(a = (def arg_method; end))
|
||||
def body_method; end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
obj = DefSpecNested::OBJ
|
||||
obj.create_method_in_instance_eval
|
||||
|
||||
obj.should have_method :arg_method
|
||||
obj.should have_method :body_method
|
||||
|
||||
DefSpecNested.should_not have_instance_method :arg_method
|
||||
DefSpecNested.should_not have_instance_method :body_method
|
||||
end
|
||||
|
||||
it "defines methods as public by default" do
|
||||
cls = Class.new do
|
||||
def do_def
|
||||
def new_def
|
||||
1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
obj = cls.new
|
||||
obj.do_def
|
||||
obj.new_def.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "A method definition inside an instance_eval" do
|
||||
it "creates a singleton method" do
|
||||
obj = Object.new
|
||||
obj.instance_eval do
|
||||
def an_instance_eval_method;self;end
|
||||
end
|
||||
obj.an_instance_eval_method.should == obj
|
||||
|
||||
other = Object.new
|
||||
lambda { other.an_instance_eval_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "creates a singleton method when evaluated inside a metaclass" do
|
||||
obj = Object.new
|
||||
obj.instance_eval do
|
||||
class << self
|
||||
def a_metaclass_eval_method;self;end
|
||||
end
|
||||
end
|
||||
obj.a_metaclass_eval_method.should == obj
|
||||
|
||||
other = Object.new
|
||||
lambda { other.a_metaclass_eval_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "creates a class method when the receiver is a class" do
|
||||
DefSpecNested.instance_eval do
|
||||
def an_instance_eval_class_method;self;end
|
||||
end
|
||||
|
||||
DefSpecNested.an_instance_eval_class_method.should == DefSpecNested
|
||||
lambda { Object.an_instance_eval_class_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "creates a class method when the receiver is an anonymous class" do
|
||||
m = Class.new
|
||||
m.instance_eval do
|
||||
def klass_method
|
||||
:test
|
||||
end
|
||||
end
|
||||
|
||||
m.klass_method.should == :test
|
||||
lambda { Object.klass_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "creates a class method when instance_eval is within class" do
|
||||
m = Class.new do
|
||||
instance_eval do
|
||||
def klass_method
|
||||
:test
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
m.klass_method.should == :test
|
||||
lambda { Object.klass_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "A method definition inside an instance_exec" do
|
||||
it "creates a class method when the receiver is a class" do
|
||||
DefSpecNested.instance_exec(1) do |param|
|
||||
@stuff = param
|
||||
|
||||
def an_instance_exec_class_method; @stuff; end
|
||||
end
|
||||
|
||||
DefSpecNested.an_instance_exec_class_method.should == 1
|
||||
lambda { Object.an_instance_exec_class_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "creates a class method when the receiver is an anonymous class" do
|
||||
m = Class.new
|
||||
m.instance_exec(1) do |param|
|
||||
@stuff = param
|
||||
|
||||
def klass_method
|
||||
@stuff
|
||||
end
|
||||
end
|
||||
|
||||
m.klass_method.should == 1
|
||||
lambda { Object.klass_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "creates a class method when instance_exec is within class" do
|
||||
m = Class.new do
|
||||
instance_exec(2) do |param|
|
||||
@stuff = param
|
||||
|
||||
def klass_method
|
||||
@stuff
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
m.klass_method.should == 2
|
||||
lambda { Object.klass_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "A method definition in an eval" do
|
||||
it "creates an instance method" do
|
||||
class DefSpecNested
|
||||
def eval_instance_method
|
||||
eval "def an_eval_instance_method;self;end", binding
|
||||
an_eval_instance_method
|
||||
end
|
||||
end
|
||||
|
||||
obj = DefSpecNested.new
|
||||
obj.eval_instance_method.should == obj
|
||||
obj.an_eval_instance_method.should == obj
|
||||
|
||||
other = DefSpecNested.new
|
||||
other.an_eval_instance_method.should == other
|
||||
|
||||
lambda { Object.new.an_eval_instance_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "creates a class method" do
|
||||
class DefSpecNestedB
|
||||
class << self
|
||||
def eval_class_method
|
||||
eval "def an_eval_class_method;self;end" #, binding
|
||||
an_eval_class_method
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
DefSpecNestedB.eval_class_method.should == DefSpecNestedB
|
||||
DefSpecNestedB.an_eval_class_method.should == DefSpecNestedB
|
||||
|
||||
lambda { Object.an_eval_class_method }.should raise_error(NoMethodError)
|
||||
lambda { DefSpecNestedB.new.an_eval_class_method}.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "creates a singleton method" do
|
||||
class DefSpecNested
|
||||
def eval_singleton_method
|
||||
class << self
|
||||
eval "def an_eval_singleton_method;self;end", binding
|
||||
end
|
||||
an_eval_singleton_method
|
||||
end
|
||||
end
|
||||
|
||||
obj = DefSpecNested.new
|
||||
obj.eval_singleton_method.should == obj
|
||||
obj.an_eval_singleton_method.should == obj
|
||||
|
||||
other = DefSpecNested.new
|
||||
lambda { other.an_eval_singleton_method }.should raise_error(NoMethodError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "a method definition that sets more than one default parameter all to the same value" do
|
||||
def foo(a=b=c={})
|
||||
[a,b,c]
|
||||
end
|
||||
it "assigns them all the same object by default" do
|
||||
foo.should == [{},{},{}]
|
||||
a, b, c = foo
|
||||
a.should eql(b)
|
||||
a.should eql(c)
|
||||
end
|
||||
|
||||
it "allows the first argument to be given, and sets the rest to null" do
|
||||
foo(1).should == [1,nil,nil]
|
||||
end
|
||||
|
||||
it "assigns the parameters different objects across different default calls" do
|
||||
a, _b, _c = foo
|
||||
d, _e, _f = foo
|
||||
a.should_not equal(d)
|
||||
end
|
||||
|
||||
it "only allows overriding the default value of the first such parameter in each set" do
|
||||
lambda { foo(1,2) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
def bar(a=b=c=1,d=2)
|
||||
[a,b,c,d]
|
||||
end
|
||||
|
||||
it "treats the argument after the multi-parameter normally" do
|
||||
bar.should == [1,1,1,2]
|
||||
bar(3).should == [3,nil,nil,2]
|
||||
bar(3,4).should == [3,nil,nil,4]
|
||||
lambda { bar(3,4,5) }.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "The def keyword" do
|
||||
describe "within a closure" do
|
||||
it "looks outside the closure for the visibility" do
|
||||
module DefSpecsLambdaVisibility
|
||||
private
|
||||
|
||||
lambda {
|
||||
def some_method; end
|
||||
}.call
|
||||
end
|
||||
|
||||
DefSpecsLambdaVisibility.should have_private_instance_method("some_method")
|
||||
end
|
||||
end
|
||||
end
|
1132
spec/ruby/language/defined_spec.rb
Normal file
1132
spec/ruby/language/defined_spec.rb
Normal file
File diff suppressed because it is too large
Load diff
36
spec/ruby/language/encoding_spec.rb
Normal file
36
spec/ruby/language/encoding_spec.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
# -*- encoding: us-ascii -*-
|
||||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/coding_us_ascii', __FILE__)
|
||||
require File.expand_path('../fixtures/coding_utf_8', __FILE__)
|
||||
|
||||
describe "The __ENCODING__ pseudo-variable" do
|
||||
it "is an instance of Encoding" do
|
||||
__ENCODING__.should be_kind_of(Encoding)
|
||||
end
|
||||
|
||||
it "is US-ASCII by default" do
|
||||
__ENCODING__.should == Encoding::US_ASCII
|
||||
end
|
||||
|
||||
it "is the evaluated strings's one inside an eval" do
|
||||
eval("__ENCODING__".force_encoding("US-ASCII")).should == Encoding::US_ASCII
|
||||
eval("__ENCODING__".force_encoding("ASCII-8BIT")).should == Encoding::ASCII_8BIT
|
||||
end
|
||||
|
||||
it "is the encoding specified by a magic comment inside an eval" do
|
||||
code = "# encoding: ASCII-8BIT\n__ENCODING__".force_encoding("US-ASCII")
|
||||
eval(code).should == Encoding::ASCII_8BIT
|
||||
|
||||
code = "# encoding: us-ascii\n__ENCODING__".force_encoding("ASCII-8BIT")
|
||||
eval(code).should == Encoding::US_ASCII
|
||||
end
|
||||
|
||||
it "is the encoding specified by a magic comment in the file" do
|
||||
CodingUS_ASCII.encoding.should == Encoding::US_ASCII
|
||||
CodingUTF_8.encoding.should == Encoding::UTF_8
|
||||
end
|
||||
|
||||
it "raises a SyntaxError if assigned to" do
|
||||
lambda { eval("__ENCODING__ = 1") }.should raise_error(SyntaxError)
|
||||
end
|
||||
end
|
126
spec/ruby/language/ensure_spec.rb
Normal file
126
spec/ruby/language/ensure_spec.rb
Normal file
|
@ -0,0 +1,126 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/ensure', __FILE__)
|
||||
|
||||
describe "An ensure block inside a begin block" do
|
||||
before :each do
|
||||
ScratchPad.record []
|
||||
end
|
||||
|
||||
it "is executed when an exception is raised in it's corresponding begin block" do
|
||||
begin
|
||||
lambda {
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
raise "An exception occured!"
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
}.should raise_error(RuntimeError)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
end
|
||||
|
||||
it "is executed when an exception is raised and rescued in it's corresponding begin block" do
|
||||
begin
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
raise "An exception occured!"
|
||||
rescue
|
||||
ScratchPad << :rescue
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :rescue, :ensure]
|
||||
end
|
||||
end
|
||||
|
||||
it "is executed even when a symbol is thrown in it's corresponding begin block" do
|
||||
begin
|
||||
catch(:symbol) do
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
throw(:symbol)
|
||||
rescue
|
||||
ScratchPad << :rescue
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
end
|
||||
|
||||
it "is executed when nothing is raised or thrown in it's corresponding begin block" do
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
rescue
|
||||
ScratchPad << :rescue
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
|
||||
it "has no return value" do
|
||||
begin
|
||||
:begin
|
||||
ensure
|
||||
:ensure
|
||||
end.should == :begin
|
||||
end
|
||||
end
|
||||
|
||||
describe "The value of an ensure expression," do
|
||||
it "in no-exception scenarios, is the value of the last statement of the protected body" do
|
||||
begin
|
||||
v = 1
|
||||
eval('x=1') # to prevent opts from triggering
|
||||
v
|
||||
ensure
|
||||
v = 2
|
||||
end.should == 1
|
||||
end
|
||||
|
||||
it "when an exception is rescued, is the value of the rescuing block" do
|
||||
begin
|
||||
raise 'foo'
|
||||
rescue
|
||||
v = 3
|
||||
ensure
|
||||
v = 2
|
||||
end.should == 3
|
||||
end
|
||||
end
|
||||
|
||||
describe "An ensure block inside a method" do
|
||||
before :each do
|
||||
@obj = EnsureSpec::Container.new
|
||||
end
|
||||
|
||||
it "is executed when an exception is raised in the method" do
|
||||
lambda { @obj.raise_in_method_with_ensure }.should raise_error(RuntimeError)
|
||||
@obj.executed.should == [:method, :ensure]
|
||||
end
|
||||
|
||||
it "is executed when an exception is raised and rescued in the method" do
|
||||
@obj.raise_and_rescue_in_method_with_ensure
|
||||
@obj.executed.should == [:method, :rescue, :ensure]
|
||||
end
|
||||
|
||||
it "is executed even when a symbol is thrown in the method" do
|
||||
catch(:symbol) { @obj.throw_in_method_with_ensure }
|
||||
@obj.executed.should == [:method, :ensure]
|
||||
end
|
||||
|
||||
it "has no impact on the method's implicit return value" do
|
||||
@obj.implicit_return_in_method_with_ensure.should == :method
|
||||
end
|
||||
|
||||
it "has an impact on the method's explicit return value" do
|
||||
@obj.explicit_return_in_method_with_ensure.should == :ensure
|
||||
end
|
||||
end
|
15
spec/ruby/language/execution_spec.rb
Normal file
15
spec/ruby/language/execution_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "``" do
|
||||
it "returns the output of the executed sub-process" do
|
||||
ip = 'world'
|
||||
`echo disc #{ip}`.should == "disc world\n"
|
||||
end
|
||||
end
|
||||
|
||||
describe "%x" do
|
||||
it "is the same as ``" do
|
||||
ip = 'world'
|
||||
%x(echo disc #{ip}).should == "disc world\n"
|
||||
end
|
||||
end
|
29
spec/ruby/language/file_spec.rb
Normal file
29
spec/ruby/language/file_spec.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/code_loading', __FILE__)
|
||||
require File.expand_path('../shared/__FILE__', __FILE__)
|
||||
|
||||
describe "The __FILE__ pseudo-variable" do
|
||||
it "raises a SyntaxError if assigned to" do
|
||||
lambda { eval("__FILE__ = 1") }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "equals (eval) inside an eval" do
|
||||
eval("__FILE__").should == "(eval)"
|
||||
end
|
||||
end
|
||||
|
||||
describe "The __FILE__ pseudo-variable" do
|
||||
it_behaves_like :language___FILE__, :require, CodeLoadingSpecs::Method.new
|
||||
end
|
||||
|
||||
describe "The __FILE__ pseudo-variable" do
|
||||
it_behaves_like :language___FILE__, :require, Kernel
|
||||
end
|
||||
|
||||
describe "The __FILE__ pseudo-variable" do
|
||||
it_behaves_like :language___FILE__, :load, CodeLoadingSpecs::Method.new
|
||||
end
|
||||
|
||||
describe "The __FILE__ pseudo-variable" do
|
||||
it_behaves_like :language___FILE__, :load, Kernel
|
||||
end
|
1
spec/ruby/language/fixtures/argv_encoding.rb
Normal file
1
spec/ruby/language/fixtures/argv_encoding.rb
Normal file
|
@ -0,0 +1 @@
|
|||
p ARGV.map { |a| a.encoding.name }
|
11
spec/ruby/language/fixtures/array.rb
Normal file
11
spec/ruby/language/fixtures/array.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
module ArraySpec
|
||||
class Splat
|
||||
def unpack_3args(a, b, c)
|
||||
[a, b, c]
|
||||
end
|
||||
|
||||
def unpack_4args(a, b, c, d)
|
||||
[a, b, c, d]
|
||||
end
|
||||
end
|
||||
end
|
57
spec/ruby/language/fixtures/block.rb
Normal file
57
spec/ruby/language/fixtures/block.rb
Normal file
|
@ -0,0 +1,57 @@
|
|||
module BlockSpecs
|
||||
class Yielder
|
||||
def z
|
||||
yield
|
||||
end
|
||||
|
||||
def m(*a)
|
||||
yield(*a)
|
||||
end
|
||||
|
||||
def s(a)
|
||||
yield(a)
|
||||
end
|
||||
|
||||
def r(a)
|
||||
yield(*a)
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: rewrite all specs that use Yield to use Yielder
|
||||
|
||||
class Yield
|
||||
def splat(*args)
|
||||
yield(*args)
|
||||
end
|
||||
|
||||
def two_args
|
||||
yield 1, 2
|
||||
end
|
||||
|
||||
def two_arg_array
|
||||
yield [1, 2]
|
||||
end
|
||||
|
||||
def yield_splat_inside_block
|
||||
[1, 2].send(:each_with_index) {|*args| yield(*args)}
|
||||
end
|
||||
|
||||
def yield_this(obj)
|
||||
yield obj
|
||||
end
|
||||
end
|
||||
|
||||
class OverwriteBlockVariable
|
||||
def initialize
|
||||
@y = Yielder.new
|
||||
end
|
||||
|
||||
def method_missing(method, *args, &block)
|
||||
self.class.send :define_method, method do |*a, &b|
|
||||
@y.send method, *a, &b
|
||||
end
|
||||
|
||||
send method, *args, &block
|
||||
end
|
||||
end
|
||||
end
|
263
spec/ruby/language/fixtures/break.rb
Normal file
263
spec/ruby/language/fixtures/break.rb
Normal file
|
@ -0,0 +1,263 @@
|
|||
module BreakSpecs
|
||||
class Driver
|
||||
def initialize(ensures=false)
|
||||
@ensures = ensures
|
||||
end
|
||||
|
||||
def note(value)
|
||||
ScratchPad << value
|
||||
end
|
||||
end
|
||||
|
||||
class Block < Driver
|
||||
def break_nil
|
||||
note :a
|
||||
note yielding {
|
||||
note :b
|
||||
break
|
||||
note :c
|
||||
}
|
||||
note :d
|
||||
end
|
||||
|
||||
def break_value
|
||||
note :a
|
||||
note yielding {
|
||||
note :b
|
||||
break :break
|
||||
note :c
|
||||
}
|
||||
note :d
|
||||
end
|
||||
|
||||
def yielding
|
||||
note :aa
|
||||
note yield
|
||||
note :bb
|
||||
end
|
||||
|
||||
def create_block
|
||||
note :za
|
||||
b = capture_block do
|
||||
note :zb
|
||||
break :break
|
||||
note :zc
|
||||
end
|
||||
note :zd
|
||||
b
|
||||
end
|
||||
|
||||
def capture_block(&b)
|
||||
note :xa
|
||||
b
|
||||
end
|
||||
|
||||
def break_in_method_captured
|
||||
note :a
|
||||
create_block.call
|
||||
note :b
|
||||
end
|
||||
|
||||
def break_in_yield_captured
|
||||
note :a
|
||||
yielding(&create_block)
|
||||
note :b
|
||||
end
|
||||
|
||||
def break_in_method
|
||||
note :a
|
||||
b = capture_block {
|
||||
note :b
|
||||
break :break
|
||||
note :c
|
||||
}
|
||||
note :d
|
||||
note b.call
|
||||
note :e
|
||||
end
|
||||
|
||||
def call_method(b)
|
||||
note :aa
|
||||
note b.call
|
||||
note :bb
|
||||
end
|
||||
|
||||
def break_in_nested_method
|
||||
note :a
|
||||
b = capture_block {
|
||||
note :b
|
||||
break :break
|
||||
note :c
|
||||
}
|
||||
note :cc
|
||||
note call_method(b)
|
||||
note :d
|
||||
end
|
||||
|
||||
def break_in_yielding_method
|
||||
note :a
|
||||
b = capture_block {
|
||||
note :b
|
||||
break :break
|
||||
note :c
|
||||
}
|
||||
note :cc
|
||||
note yielding(&b)
|
||||
note :d
|
||||
end
|
||||
|
||||
def method(v)
|
||||
yield v
|
||||
end
|
||||
|
||||
def invoke_yield_in_while
|
||||
looping = true
|
||||
while looping
|
||||
note :aa
|
||||
yield
|
||||
note :bb
|
||||
looping = false
|
||||
end
|
||||
note :should_not_reach_here
|
||||
end
|
||||
|
||||
def break_in_block_in_while
|
||||
invoke_yield_in_while do
|
||||
note :break
|
||||
break :value
|
||||
note :c
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Lambda < Driver
|
||||
# Cases for the invocation of the scope defining the lambda still active
|
||||
# on the call stack when the lambda is invoked.
|
||||
def break_in_defining_scope(value=true)
|
||||
note :a
|
||||
note lambda {
|
||||
note :b
|
||||
if value
|
||||
break :break
|
||||
else
|
||||
break
|
||||
end
|
||||
note :c
|
||||
}.call
|
||||
note :d
|
||||
end
|
||||
|
||||
def break_in_nested_scope
|
||||
note :a
|
||||
l = lambda do
|
||||
note :b
|
||||
break :break
|
||||
note :c
|
||||
end
|
||||
note :d
|
||||
|
||||
invoke_lambda l
|
||||
|
||||
note :e
|
||||
end
|
||||
|
||||
def invoke_lambda(l)
|
||||
note :aa
|
||||
note l.call
|
||||
note :bb
|
||||
end
|
||||
|
||||
def break_in_nested_scope_yield
|
||||
note :a
|
||||
l = lambda do
|
||||
note :b
|
||||
break :break
|
||||
note :c
|
||||
end
|
||||
note :d
|
||||
|
||||
invoke_yield(&l)
|
||||
|
||||
note :e
|
||||
end
|
||||
|
||||
def note_invoke_yield
|
||||
note :aa
|
||||
note yield
|
||||
note :bb
|
||||
end
|
||||
|
||||
def break_in_nested_scope_block
|
||||
note :a
|
||||
l = lambda do
|
||||
note :b
|
||||
break :break
|
||||
note :c
|
||||
end
|
||||
note :d
|
||||
|
||||
invoke_lambda_block l
|
||||
|
||||
note :e
|
||||
end
|
||||
|
||||
def invoke_yield
|
||||
note :aaa
|
||||
yield
|
||||
note :bbb
|
||||
end
|
||||
|
||||
def invoke_lambda_block(b)
|
||||
note :aa
|
||||
invoke_yield do
|
||||
note :bb
|
||||
|
||||
note b.call
|
||||
|
||||
note :cc
|
||||
end
|
||||
note :dd
|
||||
end
|
||||
|
||||
# Cases for the invocation of the scope defining the lambda NOT still
|
||||
# active on the call stack when the lambda is invoked.
|
||||
def create_lambda
|
||||
note :la
|
||||
l = lambda do
|
||||
note :lb
|
||||
break :break
|
||||
note :lc
|
||||
end
|
||||
note :ld
|
||||
l
|
||||
end
|
||||
|
||||
def break_in_method
|
||||
note :a
|
||||
|
||||
note create_lambda.call
|
||||
|
||||
note :b
|
||||
end
|
||||
|
||||
def break_in_block_in_method
|
||||
note :a
|
||||
invoke_yield do
|
||||
note :b
|
||||
|
||||
note create_lambda.call
|
||||
|
||||
note :c
|
||||
end
|
||||
note :d
|
||||
end
|
||||
|
||||
def break_in_method_yield
|
||||
note :a
|
||||
|
||||
invoke_yield(&create_lambda)
|
||||
|
||||
note :b
|
||||
end
|
||||
end
|
||||
end
|
9
spec/ruby/language/fixtures/break_lambda_toplevel.rb
Normal file
9
spec/ruby/language/fixtures/break_lambda_toplevel.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
print "a,"
|
||||
|
||||
print lambda {
|
||||
print "b,"
|
||||
break "break,"
|
||||
print "c,"
|
||||
}.call
|
||||
|
||||
puts "d"
|
23
spec/ruby/language/fixtures/break_lambda_toplevel_block.rb
Normal file
23
spec/ruby/language/fixtures/break_lambda_toplevel_block.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
print "a,"
|
||||
|
||||
l = lambda {
|
||||
print "b,"
|
||||
break "break,"
|
||||
print "c,"
|
||||
}
|
||||
|
||||
def a(l)
|
||||
print "d,"
|
||||
b { l.call }
|
||||
print "e,"
|
||||
end
|
||||
|
||||
def b
|
||||
print "f,"
|
||||
print yield
|
||||
print "g,"
|
||||
end
|
||||
|
||||
a(l)
|
||||
|
||||
puts "h"
|
17
spec/ruby/language/fixtures/break_lambda_toplevel_method.rb
Normal file
17
spec/ruby/language/fixtures/break_lambda_toplevel_method.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
print "a,"
|
||||
|
||||
l = lambda {
|
||||
print "b,"
|
||||
break "break,"
|
||||
print "c,"
|
||||
}
|
||||
|
||||
def a(l)
|
||||
print "d,"
|
||||
print l.call
|
||||
print "e,"
|
||||
end
|
||||
|
||||
a(l)
|
||||
|
||||
puts "f"
|
31
spec/ruby/language/fixtures/classes.rb
Normal file
31
spec/ruby/language/fixtures/classes.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
module LanguageSpecs
|
||||
# Regexp support
|
||||
|
||||
def self.paired_delimiters
|
||||
[%w[( )], %w[{ }], %w[< >], ["[", "]"]]
|
||||
end
|
||||
|
||||
def self.non_paired_delimiters
|
||||
%w[~ ! # $ % ^ & * _ + ` - = " ' , . ? / | \\]
|
||||
end
|
||||
|
||||
def self.blanks
|
||||
" \t"
|
||||
end
|
||||
|
||||
def self.white_spaces
|
||||
return blanks + "\f\n\r\v"
|
||||
end
|
||||
|
||||
def self.non_alphanum_non_space
|
||||
'~!@#$%^&*()+-\|{}[]:";\'<>?,./'
|
||||
end
|
||||
|
||||
def self.punctuations
|
||||
",.?" # TODO - Need to fill in the full list
|
||||
end
|
||||
|
||||
def self.get_regexp_with_substitution o
|
||||
/#{o}/o
|
||||
end
|
||||
end
|
11
spec/ruby/language/fixtures/coding_us_ascii.rb
Normal file
11
spec/ruby/language/fixtures/coding_us_ascii.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# encoding: us-ascii
|
||||
|
||||
module CodingUS_ASCII
|
||||
def self.encoding
|
||||
__ENCODING__
|
||||
end
|
||||
|
||||
def self.string_literal
|
||||
"string literal"
|
||||
end
|
||||
end
|
11
spec/ruby/language/fixtures/coding_utf_8.rb
Normal file
11
spec/ruby/language/fixtures/coding_utf_8.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# encoding: utf-8
|
||||
|
||||
module CodingUTF_8
|
||||
def self.encoding
|
||||
__ENCODING__
|
||||
end
|
||||
|
||||
def self.string_literal
|
||||
"string literal"
|
||||
end
|
||||
end
|
98
spec/ruby/language/fixtures/constant_visibility.rb
Normal file
98
spec/ruby/language/fixtures/constant_visibility.rb
Normal file
|
@ -0,0 +1,98 @@
|
|||
module ConstantVisibility
|
||||
module ModuleContainer
|
||||
module PrivateModule
|
||||
end
|
||||
private_constant :PrivateModule
|
||||
|
||||
class PrivateClass
|
||||
end
|
||||
private_constant :PrivateClass
|
||||
end
|
||||
|
||||
class ClassContainer
|
||||
module PrivateModule
|
||||
end
|
||||
private_constant :PrivateModule
|
||||
|
||||
class PrivateClass
|
||||
end
|
||||
private_constant :PrivateClass
|
||||
end
|
||||
|
||||
module PrivConstModule
|
||||
PRIVATE_CONSTANT_MODULE = true
|
||||
private_constant :PRIVATE_CONSTANT_MODULE
|
||||
|
||||
def self.private_constant_from_self
|
||||
PRIVATE_CONSTANT_MODULE
|
||||
end
|
||||
|
||||
def self.defined_from_self
|
||||
defined? PRIVATE_CONSTANT_MODULE
|
||||
end
|
||||
|
||||
module Nested
|
||||
def self.private_constant_from_scope
|
||||
PRIVATE_CONSTANT_MODULE
|
||||
end
|
||||
|
||||
def self.defined_from_scope
|
||||
defined? PRIVATE_CONSTANT_MODULE
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class PrivConstClass
|
||||
PRIVATE_CONSTANT_CLASS = true
|
||||
private_constant :PRIVATE_CONSTANT_CLASS
|
||||
|
||||
def self.private_constant_from_self
|
||||
PRIVATE_CONSTANT_CLASS
|
||||
end
|
||||
|
||||
def self.defined_from_self
|
||||
defined? PRIVATE_CONSTANT_CLASS
|
||||
end
|
||||
|
||||
module Nested
|
||||
def self.private_constant_from_scope
|
||||
PRIVATE_CONSTANT_CLASS
|
||||
end
|
||||
|
||||
def self.defined_from_scope
|
||||
defined? PRIVATE_CONSTANT_CLASS
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class PrivConstModuleChild
|
||||
include PrivConstModule
|
||||
|
||||
def private_constant_from_include
|
||||
PRIVATE_CONSTANT_MODULE
|
||||
end
|
||||
|
||||
def defined_from_include
|
||||
defined? PRIVATE_CONSTANT_MODULE
|
||||
end
|
||||
end
|
||||
|
||||
class PrivConstClassChild < PrivConstClass
|
||||
def private_constant_from_subclass
|
||||
PRIVATE_CONSTANT_CLASS
|
||||
end
|
||||
|
||||
def defined_from_subclass
|
||||
defined? PRIVATE_CONSTANT_CLASS
|
||||
end
|
||||
end
|
||||
|
||||
def self.reset_private_constants
|
||||
Object.send :private_constant, :PRIVATE_CONSTANT_IN_OBJECT
|
||||
end
|
||||
end
|
||||
|
||||
class Object
|
||||
PRIVATE_CONSTANT_IN_OBJECT = true
|
||||
private_constant :PRIVATE_CONSTANT_IN_OBJECT
|
||||
end
|
54
spec/ruby/language/fixtures/constants_sclass.rb
Normal file
54
spec/ruby/language/fixtures/constants_sclass.rb
Normal file
|
@ -0,0 +1,54 @@
|
|||
module ConstantSpecs
|
||||
|
||||
CS_SINGLETON1 = Object.new
|
||||
class << CS_SINGLETON1
|
||||
CONST = 1
|
||||
def foo
|
||||
CONST
|
||||
end
|
||||
end
|
||||
|
||||
CS_SINGLETON2 = [Object.new, Object.new]
|
||||
2.times do |i|
|
||||
obj = CS_SINGLETON2[i]
|
||||
$spec_i = i
|
||||
class << obj
|
||||
CONST = ($spec_i + 1)
|
||||
def foo
|
||||
CONST
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
CS_SINGLETON3 = [Object.new, Object.new]
|
||||
2.times do |i|
|
||||
obj = CS_SINGLETON3[i]
|
||||
class << obj
|
||||
class X
|
||||
# creates <singleton class::X>
|
||||
end
|
||||
|
||||
def x
|
||||
X
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
CS_SINGLETON4 = [Object.new, Object.new]
|
||||
CS_SINGLETON4_CLASSES = []
|
||||
2.times do |i|
|
||||
obj = CS_SINGLETON4[i]
|
||||
$spec_i = i
|
||||
class << obj
|
||||
class X
|
||||
CS_SINGLETON4_CLASSES << self
|
||||
CONST = ($spec_i + 1)
|
||||
|
||||
def foo
|
||||
CONST
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
8
spec/ruby/language/fixtures/def.rb
Normal file
8
spec/ruby/language/fixtures/def.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
def some_toplevel_method
|
||||
end
|
||||
|
||||
public
|
||||
def public_toplevel_method
|
||||
end
|
||||
|
||||
private
|
298
spec/ruby/language/fixtures/defined.rb
Normal file
298
spec/ruby/language/fixtures/defined.rb
Normal file
|
@ -0,0 +1,298 @@
|
|||
module DefinedSpecs
|
||||
self::SelfScoped = 42
|
||||
|
||||
def self.side_effects
|
||||
ScratchPad.record :defined_specs_side_effects
|
||||
end
|
||||
|
||||
def self.fixnum_method
|
||||
ScratchPad.record :defined_specs_fixnum_method
|
||||
42
|
||||
end
|
||||
|
||||
def self.exception_method
|
||||
ScratchPad.record :defined_specs_exception
|
||||
raise "defined? specs exception method"
|
||||
end
|
||||
|
||||
def self.defined_method
|
||||
DefinedSpecs
|
||||
end
|
||||
|
||||
class Basic
|
||||
A = 42
|
||||
|
||||
def defined_method
|
||||
DefinedSpecs
|
||||
end
|
||||
|
||||
def a_defined_method
|
||||
end
|
||||
|
||||
def protected_method
|
||||
end
|
||||
protected :protected_method
|
||||
|
||||
def private_method
|
||||
end
|
||||
private :private_method
|
||||
|
||||
def private_method_defined
|
||||
defined? private_method
|
||||
end
|
||||
|
||||
def private_predicate?
|
||||
end
|
||||
private :private_predicate?
|
||||
|
||||
def private_predicate_defined
|
||||
defined? private_predicate?
|
||||
end
|
||||
|
||||
def local_variable_defined
|
||||
x = 2
|
||||
defined? x
|
||||
end
|
||||
|
||||
def local_variable_defined_nil
|
||||
x = nil
|
||||
defined? x
|
||||
end
|
||||
|
||||
def instance_variable_undefined
|
||||
defined? @instance_variable_undefined
|
||||
end
|
||||
|
||||
def instance_variable_read
|
||||
value = @instance_variable_read
|
||||
defined? @instance_variable_read
|
||||
end
|
||||
|
||||
def instance_variable_defined
|
||||
@instance_variable_defined = 1
|
||||
defined? @instance_variable_defined
|
||||
end
|
||||
|
||||
def instance_variable_defined_nil
|
||||
@instance_variable_defined_nil = nil
|
||||
defined? @instance_variable_defined_nil
|
||||
end
|
||||
|
||||
def global_variable_undefined
|
||||
defined? $defined_specs_global_variable_undefined
|
||||
end
|
||||
|
||||
def global_variable_read
|
||||
suppress_warning do
|
||||
value = $defined_specs_global_variable_read
|
||||
end
|
||||
defined? $defined_specs_global_variable_read
|
||||
end
|
||||
|
||||
def global_variable_defined
|
||||
$defined_specs_global_variable_defined = 1
|
||||
defined? $defined_specs_global_variable_defined
|
||||
end
|
||||
|
||||
def class_variable_undefined
|
||||
defined? @@class_variable_undefined
|
||||
end
|
||||
|
||||
def class_variable_defined
|
||||
@@class_variable_defined = 1
|
||||
defined? @@class_variable_defined
|
||||
end
|
||||
|
||||
def yield_defined_method
|
||||
defined? yield
|
||||
end
|
||||
|
||||
def yield_defined_parameter_method(&block)
|
||||
defined? yield
|
||||
end
|
||||
|
||||
def no_yield_block
|
||||
yield_defined_method
|
||||
end
|
||||
|
||||
def no_yield_block_parameter
|
||||
yield_defined_parameter_method
|
||||
end
|
||||
|
||||
def yield_block
|
||||
yield_defined_method { 42 }
|
||||
end
|
||||
|
||||
def yield_block_parameter
|
||||
yield_defined_parameter_method { 42 }
|
||||
end
|
||||
end
|
||||
|
||||
module Mixin
|
||||
MixinConstant = 42
|
||||
|
||||
def defined_super
|
||||
defined? super()
|
||||
end
|
||||
end
|
||||
|
||||
class Parent
|
||||
ParentConstant = 42
|
||||
|
||||
def defined_super; end
|
||||
end
|
||||
|
||||
class Child < Parent
|
||||
include Mixin
|
||||
|
||||
A = 42
|
||||
|
||||
def self.parent_constant_defined
|
||||
defined? self::ParentConstant
|
||||
end
|
||||
|
||||
def self.module_defined
|
||||
defined? Mixin
|
||||
end
|
||||
|
||||
def self.module_constant_defined
|
||||
defined? MixinConstant
|
||||
end
|
||||
|
||||
def defined_super
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
class Superclass
|
||||
def yield_method
|
||||
yield
|
||||
end
|
||||
|
||||
def method_no_args
|
||||
end
|
||||
|
||||
def method_args
|
||||
end
|
||||
|
||||
def method_block_no_args
|
||||
end
|
||||
|
||||
def method_block_args
|
||||
end
|
||||
|
||||
def define_method_no_args
|
||||
end
|
||||
|
||||
def define_method_args
|
||||
end
|
||||
|
||||
def define_method_block_no_args
|
||||
end
|
||||
|
||||
def define_method_block_args
|
||||
end
|
||||
end
|
||||
|
||||
class Super < Superclass
|
||||
def no_super_method_no_args
|
||||
defined? super
|
||||
end
|
||||
|
||||
def no_super_method_args
|
||||
defined? super()
|
||||
end
|
||||
|
||||
def method_no_args
|
||||
defined? super
|
||||
end
|
||||
|
||||
def method_args
|
||||
defined? super()
|
||||
end
|
||||
|
||||
def no_super_method_block_no_args
|
||||
yield_method { defined? super }
|
||||
end
|
||||
|
||||
def no_super_method_block_args
|
||||
yield_method { defined? super() }
|
||||
end
|
||||
|
||||
def method_block_no_args
|
||||
yield_method { defined? super }
|
||||
end
|
||||
|
||||
def method_block_args
|
||||
yield_method { defined? super() }
|
||||
end
|
||||
|
||||
define_method(:no_super_define_method_no_args) { defined? super }
|
||||
define_method(:no_super_define_method_args) { defined? super() }
|
||||
define_method(:define_method_no_args) { defined? super }
|
||||
define_method(:define_method_args) { defined? super() }
|
||||
|
||||
define_method(:no_super_define_method_block_no_args) do
|
||||
yield_method { defined? super }
|
||||
end
|
||||
|
||||
define_method(:no_super_define_method_block_args) do
|
||||
yield_method { defined? super() }
|
||||
end
|
||||
|
||||
define_method(:define_method_block_no_args) do
|
||||
yield_method { defined? super }
|
||||
end
|
||||
|
||||
define_method(:define_method_block_args) do
|
||||
yield_method { defined? super() }
|
||||
end
|
||||
end
|
||||
|
||||
class ClassWithMethod
|
||||
def test
|
||||
end
|
||||
end
|
||||
|
||||
class ClassUndefiningMethod < ClassWithMethod
|
||||
undef :test
|
||||
end
|
||||
|
||||
class ClassWithoutMethod < ClassUndefiningMethod
|
||||
# If an undefined method overridden in descendants
|
||||
# define?(super) should return nil
|
||||
def test
|
||||
defined?(super)
|
||||
end
|
||||
end
|
||||
|
||||
module IntermediateModule1
|
||||
def method_no_args
|
||||
end
|
||||
end
|
||||
|
||||
module IntermediateModule2
|
||||
def method_no_args
|
||||
defined?(super)
|
||||
end
|
||||
end
|
||||
|
||||
class SuperWithIntermediateModules
|
||||
include IntermediateModule1
|
||||
include IntermediateModule2
|
||||
|
||||
def method_no_args
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Object
|
||||
def defined_specs_method
|
||||
DefinedSpecs
|
||||
end
|
||||
|
||||
def defined_specs_receiver
|
||||
DefinedSpecs::Basic.new
|
||||
end
|
||||
end
|
6
spec/ruby/language/fixtures/dollar_zero.rb
Normal file
6
spec/ruby/language/fixtures/dollar_zero.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
puts $0
|
||||
puts __FILE__
|
||||
|
||||
if $0 == __FILE__
|
||||
print "OK"
|
||||
end
|
72
spec/ruby/language/fixtures/ensure.rb
Normal file
72
spec/ruby/language/fixtures/ensure.rb
Normal file
|
@ -0,0 +1,72 @@
|
|||
module EnsureSpec
|
||||
class Container
|
||||
attr_reader :executed
|
||||
|
||||
def initialize
|
||||
@executed = []
|
||||
end
|
||||
|
||||
def raise_in_method_with_ensure
|
||||
@executed << :method
|
||||
raise "An Exception"
|
||||
ensure
|
||||
@executed << :ensure
|
||||
end
|
||||
|
||||
def raise_and_rescue_in_method_with_ensure
|
||||
@executed << :method
|
||||
raise "An Exception"
|
||||
rescue
|
||||
@executed << :rescue
|
||||
ensure
|
||||
@executed << :ensure
|
||||
end
|
||||
|
||||
def throw_in_method_with_ensure
|
||||
@executed << :method
|
||||
throw(:symbol)
|
||||
ensure
|
||||
@executed << :ensure
|
||||
end
|
||||
|
||||
def implicit_return_in_method_with_ensure
|
||||
:method
|
||||
ensure
|
||||
:ensure
|
||||
end
|
||||
|
||||
def explicit_return_in_method_with_ensure
|
||||
return :method
|
||||
ensure
|
||||
return :ensure
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module EnsureSpec
|
||||
|
||||
class Test
|
||||
|
||||
def initialize
|
||||
@values = []
|
||||
end
|
||||
|
||||
attr_reader :values
|
||||
|
||||
def call_block
|
||||
begin
|
||||
@values << :start
|
||||
yield
|
||||
ensure
|
||||
@values << :end
|
||||
end
|
||||
end
|
||||
|
||||
def do_test
|
||||
call_block do
|
||||
@values << :in_block
|
||||
return :did_test
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
1
spec/ruby/language/fixtures/file.rb
Normal file
1
spec/ruby/language/fixtures/file.rb
Normal file
|
@ -0,0 +1 @@
|
|||
ScratchPad.record __FILE__
|
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'freeze_magic_comment_required'
|
||||
|
||||
p "abc".object_id == $second_literal_id
|
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'freeze_magic_comment_required_diff_enc'
|
||||
|
||||
p "abc".object_id != $second_literal_id
|
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'freeze_magic_comment_required_no_comment'
|
||||
|
||||
p "abc".object_id != $second_literal_id
|
|
@ -0,0 +1,4 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
ids = Array.new(2) { "abc".object_id }
|
||||
p ids.first == ids.last
|
|
@ -0,0 +1,3 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
$second_literal_id = "abc".object_id
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
$second_literal_id = "abc".object_id
|
|
@ -0,0 +1,3 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
p "abc".object_id == "abc".object_id
|
7
spec/ruby/language/fixtures/hash_strings_ascii8bit.rb
Normal file
7
spec/ruby/language/fixtures/hash_strings_ascii8bit.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# encoding: ascii-8bit
|
||||
|
||||
module HashStringsASCII8BIT
|
||||
def self.literal_hash
|
||||
{"foo" => "bar"}
|
||||
end
|
||||
end
|
7
spec/ruby/language/fixtures/hash_strings_usascii.rb
Normal file
7
spec/ruby/language/fixtures/hash_strings_usascii.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# encoding: us-ascii
|
||||
|
||||
module HashStringsUSASCII
|
||||
def self.literal_hash
|
||||
{"foo" => "bar"}
|
||||
end
|
||||
end
|
7
spec/ruby/language/fixtures/hash_strings_utf8.rb
Normal file
7
spec/ruby/language/fixtures/hash_strings_utf8.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# encoding: utf-8
|
||||
|
||||
module HashStringsUTF8
|
||||
def self.literal_hash
|
||||
{"foo" => "bar"}
|
||||
end
|
||||
end
|
9
spec/ruby/language/fixtures/match_operators.rb
Normal file
9
spec/ruby/language/fixtures/match_operators.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
class OperatorImplementor
|
||||
def =~(val)
|
||||
return val
|
||||
end
|
||||
|
||||
def !~(val)
|
||||
return val
|
||||
end
|
||||
end
|
34
spec/ruby/language/fixtures/metaclass.rb
Normal file
34
spec/ruby/language/fixtures/metaclass.rb
Normal file
|
@ -0,0 +1,34 @@
|
|||
module MetaClassSpecs
|
||||
|
||||
def self.metaclass_of obj
|
||||
class << obj
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
class A
|
||||
def self.cheese
|
||||
'edam'
|
||||
end
|
||||
end
|
||||
|
||||
class B < A
|
||||
def self.cheese
|
||||
'stilton'
|
||||
end
|
||||
end
|
||||
|
||||
class C
|
||||
class << self
|
||||
class << self
|
||||
def ham
|
||||
'iberico'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class D < C; end
|
||||
|
||||
end
|
||||
|
24
spec/ruby/language/fixtures/module.rb
Normal file
24
spec/ruby/language/fixtures/module.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
module ModuleSpecs
|
||||
module Modules
|
||||
class Klass
|
||||
end
|
||||
|
||||
A = "Module"
|
||||
B = 1
|
||||
C = nil
|
||||
D = true
|
||||
E = false
|
||||
end
|
||||
|
||||
module Anonymous
|
||||
end
|
||||
|
||||
module IncludedInObject
|
||||
module IncludedModuleSpecs
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Object
|
||||
include ModuleSpecs::IncludedInObject
|
||||
end
|
134
spec/ruby/language/fixtures/next.rb
Normal file
134
spec/ruby/language/fixtures/next.rb
Normal file
|
@ -0,0 +1,134 @@
|
|||
class NextSpecs
|
||||
def self.yielding_method(expected)
|
||||
yield.should == expected
|
||||
:method_return_value
|
||||
end
|
||||
|
||||
def self.yielding
|
||||
yield
|
||||
end
|
||||
|
||||
# The methods below are defined to spec the behavior of the next statement
|
||||
# while specifically isolating whether the statement is in an Iter block or
|
||||
# not. In a normal spec example, the code is always nested inside a block.
|
||||
# Rather than rely on that implicit context in this case, the context is
|
||||
# made explicit because of the interaction of next in a loop nested inside
|
||||
# an Iter block.
|
||||
def self.while_next(arg)
|
||||
x = true
|
||||
while x
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
x = false
|
||||
if arg
|
||||
next 42
|
||||
else
|
||||
next
|
||||
end
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.while_within_iter(arg)
|
||||
yielding do
|
||||
x = true
|
||||
while x
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
x = false
|
||||
if arg
|
||||
next 42
|
||||
else
|
||||
next
|
||||
end
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.until_next(arg)
|
||||
x = false
|
||||
until x
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
x = true
|
||||
if arg
|
||||
next 42
|
||||
else
|
||||
next
|
||||
end
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.until_within_iter(arg)
|
||||
yielding do
|
||||
x = false
|
||||
until x
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
x = true
|
||||
if arg
|
||||
next 42
|
||||
else
|
||||
next
|
||||
end
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.loop_next(arg)
|
||||
x = 1
|
||||
loop do
|
||||
break if x == 2
|
||||
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
x += 1
|
||||
if arg
|
||||
next 42
|
||||
else
|
||||
next
|
||||
end
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.loop_within_iter(arg)
|
||||
yielding do
|
||||
x = 1
|
||||
loop do
|
||||
break if x == 2
|
||||
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
x += 1
|
||||
if arg
|
||||
next 42
|
||||
else
|
||||
next
|
||||
end
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Block
|
||||
def method(v)
|
||||
yield v
|
||||
end
|
||||
end
|
||||
end
|
16
spec/ruby/language/fixtures/precedence.rb
Normal file
16
spec/ruby/language/fixtures/precedence.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
module PrecedenceSpecs
|
||||
class NonUnaryOpTest
|
||||
def add_num(arg)
|
||||
[1].collect { |i| arg + i +1 }
|
||||
end
|
||||
def sub_num(arg)
|
||||
[1].collect { |i| arg + i -1 }
|
||||
end
|
||||
def add_str
|
||||
%w[1].collect { |i| i +'1' }
|
||||
end
|
||||
def add_var
|
||||
[1].collect { |i| i +i }
|
||||
end
|
||||
end
|
||||
end
|
59
spec/ruby/language/fixtures/private.rb
Normal file
59
spec/ruby/language/fixtures/private.rb
Normal file
|
@ -0,0 +1,59 @@
|
|||
module Private
|
||||
class A
|
||||
def foo
|
||||
"foo"
|
||||
end
|
||||
|
||||
private
|
||||
def bar
|
||||
"bar"
|
||||
end
|
||||
end
|
||||
|
||||
class B
|
||||
def foo
|
||||
"foo"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.public_defs_method; 0; end
|
||||
|
||||
class C
|
||||
def baz
|
||||
"baz"
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
def public_class_method1; 1; end
|
||||
private
|
||||
def private_class_method1; 1; end
|
||||
end
|
||||
|
||||
def bar
|
||||
"bar"
|
||||
end
|
||||
end
|
||||
|
||||
module D
|
||||
private
|
||||
def foo
|
||||
"foo"
|
||||
end
|
||||
end
|
||||
|
||||
class E
|
||||
include D
|
||||
end
|
||||
|
||||
class G
|
||||
def foo
|
||||
"foo"
|
||||
end
|
||||
end
|
||||
|
||||
class H < A
|
||||
private :foo
|
||||
end
|
||||
end
|
63
spec/ruby/language/fixtures/rescue.rb
Normal file
63
spec/ruby/language/fixtures/rescue.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
module RescueSpecs
|
||||
def self.begin_else(raise_exception)
|
||||
begin
|
||||
ScratchPad << :one
|
||||
raise "an error occurred" if raise_exception
|
||||
rescue
|
||||
ScratchPad << :rescue_ran
|
||||
:rescue_val
|
||||
else
|
||||
ScratchPad << :else_ran
|
||||
:val
|
||||
end
|
||||
end
|
||||
|
||||
def self.begin_else_ensure(raise_exception)
|
||||
begin
|
||||
ScratchPad << :one
|
||||
raise "an error occurred" if raise_exception
|
||||
rescue
|
||||
ScratchPad << :rescue_ran
|
||||
:rescue_val
|
||||
else
|
||||
ScratchPad << :else_ran
|
||||
:val
|
||||
ensure
|
||||
ScratchPad << :ensure_ran
|
||||
:ensure_val
|
||||
end
|
||||
end
|
||||
|
||||
def self.begin_else_return(raise_exception)
|
||||
begin
|
||||
ScratchPad << :one
|
||||
raise "an error occurred" if raise_exception
|
||||
rescue
|
||||
ScratchPad << :rescue_ran
|
||||
:rescue_val
|
||||
else
|
||||
ScratchPad << :else_ran
|
||||
:val
|
||||
end
|
||||
ScratchPad << :outside_begin
|
||||
:return_val
|
||||
end
|
||||
|
||||
def self.begin_else_return_ensure(raise_exception)
|
||||
begin
|
||||
ScratchPad << :one
|
||||
raise "an error occurred" if raise_exception
|
||||
rescue
|
||||
ScratchPad << :rescue_ran
|
||||
:rescue_val
|
||||
else
|
||||
ScratchPad << :else_ran
|
||||
:val
|
||||
ensure
|
||||
ScratchPad << :ensure_ran
|
||||
:ensure_val
|
||||
end
|
||||
ScratchPad << :outside_begin
|
||||
:return_val
|
||||
end
|
||||
end
|
139
spec/ruby/language/fixtures/return.rb
Normal file
139
spec/ruby/language/fixtures/return.rb
Normal file
|
@ -0,0 +1,139 @@
|
|||
module ReturnSpecs
|
||||
class Blocks
|
||||
def yielding_method
|
||||
yield
|
||||
ScratchPad.record :after_yield
|
||||
end
|
||||
|
||||
def enclosing_method
|
||||
yielding_method do
|
||||
ScratchPad.record :before_return
|
||||
return :return_value
|
||||
ScratchPad.record :after_return
|
||||
end
|
||||
|
||||
ScratchPad.record :after_call
|
||||
end
|
||||
end
|
||||
|
||||
class NestedCalls < Blocks
|
||||
def invoking_method(&b)
|
||||
yielding_method(&b)
|
||||
ScratchPad.record :after_invoke
|
||||
end
|
||||
|
||||
def enclosing_method
|
||||
invoking_method do
|
||||
ScratchPad.record :before_return
|
||||
return :return_value
|
||||
ScratchPad.record :after_return
|
||||
end
|
||||
ScratchPad.record :after_invoke
|
||||
end
|
||||
end
|
||||
|
||||
class NestedBlocks < Blocks
|
||||
def enclosing_method
|
||||
yielding_method do
|
||||
yielding_method do
|
||||
ScratchPad.record :before_return
|
||||
return :return_value
|
||||
ScratchPad.record :after_return
|
||||
end
|
||||
ScratchPad.record :after_invoke1
|
||||
end
|
||||
ScratchPad.record :after_invoke2
|
||||
end
|
||||
end
|
||||
|
||||
class SavedInnerBlock
|
||||
def add(&b)
|
||||
@block = b
|
||||
end
|
||||
|
||||
def outer
|
||||
yield
|
||||
@block.call
|
||||
end
|
||||
|
||||
def inner
|
||||
yield
|
||||
end
|
||||
|
||||
def start
|
||||
outer do
|
||||
inner do
|
||||
add do
|
||||
ScratchPad.record :before_return
|
||||
return :return_value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ScratchPad.record :bottom_of_start
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
class ThroughDefineMethod
|
||||
lamb = proc { |x| x.call }
|
||||
define_method :foo, lamb
|
||||
|
||||
def mp(&b); b; end
|
||||
|
||||
def outer
|
||||
pr = mp { return :good }
|
||||
|
||||
foo(pr)
|
||||
return :bad
|
||||
end
|
||||
end
|
||||
|
||||
class DefineMethod
|
||||
lamb = proc { return :good }
|
||||
define_method :foo, lamb
|
||||
|
||||
def outer
|
||||
val = :bad
|
||||
|
||||
# This is tricky, but works. If lamb properly returns, then the
|
||||
# return value will go into val before we run the ensure.
|
||||
#
|
||||
# If lamb's return keeps unwinding incorrectly, val will still
|
||||
# have it's old value.
|
||||
#
|
||||
# We can therefore use val to figure out what happened.
|
||||
begin
|
||||
val = foo()
|
||||
ensure
|
||||
if val != :good
|
||||
return :bad
|
||||
end
|
||||
end
|
||||
|
||||
return val
|
||||
end
|
||||
end
|
||||
|
||||
class MethodWithBlock
|
||||
def method1
|
||||
return [2, 3].inject 0 do |a, b|
|
||||
a + b
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def get_ary(count, &blk)
|
||||
count.times.to_a do |i|
|
||||
blk.call(i) if blk
|
||||
end
|
||||
end
|
||||
|
||||
def method2
|
||||
return get_ary 3 do |i|
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
140
spec/ruby/language/fixtures/send.rb
Normal file
140
spec/ruby/language/fixtures/send.rb
Normal file
|
@ -0,0 +1,140 @@
|
|||
module LangSendSpecs
|
||||
module_function
|
||||
|
||||
def fooM0; 100 end
|
||||
def fooM1(a); [a]; end
|
||||
def fooM2(a,b); [a,b]; end
|
||||
def fooM3(a,b,c); [a,b,c]; end
|
||||
def fooM4(a,b,c,d); [a,b,c,d]; end
|
||||
def fooM5(a,b,c,d,e); [a,b,c,d,e]; end
|
||||
def fooM0O1(a=1); [a]; end
|
||||
def fooM1O1(a,b=1); [a,b]; end
|
||||
def fooM2O1(a,b,c=1); [a,b,c]; end
|
||||
def fooM3O1(a,b,c,d=1); [a,b,c,d]; end
|
||||
def fooM4O1(a,b,c,d,e=1); [a,b,c,d,e]; end
|
||||
def fooM0O2(a=1,b=2); [a,b]; end
|
||||
def fooM0R(*r); r; end
|
||||
def fooM1R(a, *r); [a, r]; end
|
||||
def fooM0O1R(a=1, *r); [a, r]; end
|
||||
def fooM1O1R(a, b=1, *r); [a, b, r]; end
|
||||
|
||||
def one(a); a; end
|
||||
def oneb(a,&b); [a,yield(b)]; end
|
||||
def twob(a,b,&c); [a,b,yield(c)]; end
|
||||
def makeproc(&b) b end
|
||||
|
||||
def yield_now; yield; end
|
||||
|
||||
def double(x); x * 2 end
|
||||
def weird_parens
|
||||
# means double((5).to_s)
|
||||
# NOT (double(5)).to_s
|
||||
double (5).to_s
|
||||
end
|
||||
|
||||
def rest_len(*a); a.size; end
|
||||
|
||||
def self.twos(a,b,*c)
|
||||
[c.size, c.last]
|
||||
end
|
||||
|
||||
class PrivateSetter
|
||||
attr_reader :foo
|
||||
attr_writer :foo
|
||||
private :foo=
|
||||
|
||||
def call_self_foo_equals(value)
|
||||
self.foo = value
|
||||
end
|
||||
|
||||
def call_self_foo_equals_masgn(value)
|
||||
a, self.foo = 1, value
|
||||
end
|
||||
end
|
||||
|
||||
class PrivateGetter
|
||||
attr_reader :foo
|
||||
private :foo
|
||||
|
||||
def call_self_foo
|
||||
self.foo
|
||||
end
|
||||
|
||||
def call_self_foo_or_equals(value)
|
||||
self.foo ||= 6
|
||||
end
|
||||
end
|
||||
|
||||
class AttrSet
|
||||
attr_reader :result
|
||||
def []=(a, b, c, d); @result = [a,b,c,d]; end
|
||||
end
|
||||
|
||||
class ToProc
|
||||
def initialize(val)
|
||||
@val = val
|
||||
end
|
||||
|
||||
def to_proc
|
||||
Proc.new { @val }
|
||||
end
|
||||
end
|
||||
|
||||
class ToAry
|
||||
def initialize(obj)
|
||||
@obj = obj
|
||||
end
|
||||
|
||||
def to_ary
|
||||
@obj
|
||||
end
|
||||
end
|
||||
|
||||
class MethodMissing
|
||||
def initialize
|
||||
@message = nil
|
||||
@args = nil
|
||||
end
|
||||
|
||||
attr_reader :message, :args
|
||||
|
||||
def method_missing(m, *a)
|
||||
@message = m
|
||||
@args = a
|
||||
end
|
||||
end
|
||||
|
||||
class Attr19Set
|
||||
attr_reader :result
|
||||
def []=(*args); @result = args; end
|
||||
end
|
||||
|
||||
module_function
|
||||
|
||||
def fooR(*r); r; end
|
||||
def fooM0RQ1(*r, q); [r, q]; end
|
||||
def fooM0RQ2(*r, s, q); [r, s, q]; end
|
||||
def fooM1RQ1(a, *r, q); [a, r, q]; end
|
||||
def fooM1O1RQ1(a, b=9, *r, q); [a, b, r, q]; end
|
||||
def fooM1O1RQ2(a, b=9, *r, q, t); [a, b, r, q, t]; end
|
||||
|
||||
def fooO1Q1(a=1, b); [a,b]; end
|
||||
def fooM1O1Q1(a,b=2,c); [a,b,c]; end
|
||||
def fooM2O1Q1(a,b,c=3,d); [a,b,c,d]; end
|
||||
def fooM2O2Q1(a,b,c=3,d=4,e); [a,b,c,d,e]; end
|
||||
def fooO4Q1(a=1,b=2,c=3,d=4,e); [a,b,c,d,e]; end
|
||||
def fooO4Q2(a=1,b=2,c=3,d=4,e,f); [a,b,c,d,e,f]; end
|
||||
|
||||
def destructure2((a,b)); a+b; end
|
||||
def destructure2b((a,b)); [a,b]; end
|
||||
def destructure4r((a,b,*c,d,e)); [a,b,c,d,e]; end
|
||||
def destructure4o(a=1,(b,c),d,&e); [a,b,c,d]; end
|
||||
def destructure5o(a=1, f=2, (b,c),d,&e); [a,f,b,c,d]; end
|
||||
def destructure7o(a=1, f=2, (b,c),(d,e), &g); [a,f,b,c,d,e]; end
|
||||
def destructure7b(a=1, f=2, (b,c),(d,e), &g); g.call([a,f,b,c,d,e]); end
|
||||
def destructure4os(a=1,(b,*c)); [a,b,c]; end
|
||||
end
|
||||
|
||||
def lang_send_rest_len(*a)
|
||||
a.size
|
||||
end
|
39
spec/ruby/language/fixtures/squiggly_heredoc.rb
Normal file
39
spec/ruby/language/fixtures/squiggly_heredoc.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
module SquigglyHeredocSpecs
|
||||
def self.message
|
||||
<<~HEREDOC
|
||||
character density, n.:
|
||||
The number of very weird people in the office.
|
||||
HEREDOC
|
||||
end
|
||||
|
||||
def self.blank
|
||||
<<~HERE
|
||||
HERE
|
||||
end
|
||||
|
||||
def self.unquoted
|
||||
<<~HERE
|
||||
unquoted #{"interpolated"}
|
||||
HERE
|
||||
end
|
||||
|
||||
def self.doublequoted
|
||||
<<~"HERE"
|
||||
doublequoted #{"interpolated"}
|
||||
HERE
|
||||
end
|
||||
|
||||
def self.singlequoted
|
||||
<<~'HERE'
|
||||
singlequoted #{"interpolated"}
|
||||
HERE
|
||||
end
|
||||
|
||||
def self.least_indented_on_the_last_line
|
||||
<<~HERE
|
||||
a
|
||||
b
|
||||
c
|
||||
HERE
|
||||
end
|
||||
end
|
569
spec/ruby/language/fixtures/super.rb
Normal file
569
spec/ruby/language/fixtures/super.rb
Normal file
|
@ -0,0 +1,569 @@
|
|||
module Super
|
||||
module S1
|
||||
class A
|
||||
def foo(a)
|
||||
a << "A#foo"
|
||||
bar(a)
|
||||
end
|
||||
def bar(a)
|
||||
a << "A#bar"
|
||||
end
|
||||
end
|
||||
class B < A
|
||||
def foo(a)
|
||||
a << "B#foo"
|
||||
super(a)
|
||||
end
|
||||
def bar(a)
|
||||
a << "B#bar"
|
||||
super(a)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module S2
|
||||
class A
|
||||
def baz(a)
|
||||
a << "A#baz"
|
||||
end
|
||||
end
|
||||
class B < A
|
||||
def foo(a)
|
||||
a << "B#foo"
|
||||
baz(a)
|
||||
end
|
||||
end
|
||||
class C < B
|
||||
def baz(a)
|
||||
a << "C#baz"
|
||||
super(a)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module S3
|
||||
class A
|
||||
def foo(a)
|
||||
a << "A#foo"
|
||||
end
|
||||
def self.foo(a)
|
||||
a << "A.foo"
|
||||
end
|
||||
def self.bar(a)
|
||||
a << "A.bar"
|
||||
foo(a)
|
||||
end
|
||||
end
|
||||
class B < A
|
||||
def self.foo(a)
|
||||
a << "B.foo"
|
||||
super(a)
|
||||
end
|
||||
def self.bar(a)
|
||||
a << "B.bar"
|
||||
super(a)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module S4
|
||||
class A
|
||||
def foo(a)
|
||||
a << "A#foo"
|
||||
end
|
||||
end
|
||||
class B < A
|
||||
def foo(a, b)
|
||||
a << "B#foo(a,#{b})"
|
||||
super(a)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class S5
|
||||
def here
|
||||
:good
|
||||
end
|
||||
end
|
||||
|
||||
class S6 < S5
|
||||
def under
|
||||
yield
|
||||
end
|
||||
|
||||
def here
|
||||
under {
|
||||
super
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
class S7 < S5
|
||||
define_method(:here) { super() }
|
||||
end
|
||||
|
||||
module MS1
|
||||
module ModA
|
||||
def foo(a)
|
||||
a << "ModA#foo"
|
||||
bar(a)
|
||||
end
|
||||
def bar(a)
|
||||
a << "ModA#bar"
|
||||
end
|
||||
end
|
||||
class A
|
||||
include ModA
|
||||
end
|
||||
module ModB
|
||||
def bar(a)
|
||||
a << "ModB#bar"
|
||||
super(a)
|
||||
end
|
||||
end
|
||||
class B < A
|
||||
def foo(a)
|
||||
a << "B#foo"
|
||||
super(a)
|
||||
end
|
||||
include ModB
|
||||
end
|
||||
end
|
||||
|
||||
module MS2
|
||||
class A
|
||||
def baz(a)
|
||||
a << "A#baz"
|
||||
end
|
||||
end
|
||||
module ModB
|
||||
def foo(a)
|
||||
a << "ModB#foo"
|
||||
baz(a)
|
||||
end
|
||||
end
|
||||
class B < A
|
||||
include ModB
|
||||
end
|
||||
class C < B
|
||||
def baz(a)
|
||||
a << "C#baz"
|
||||
super(a)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module MultiSuperTargets
|
||||
module M
|
||||
def foo
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
class BaseA
|
||||
def foo
|
||||
:BaseA
|
||||
end
|
||||
end
|
||||
|
||||
class BaseB
|
||||
def foo
|
||||
:BaseB
|
||||
end
|
||||
end
|
||||
|
||||
class A < BaseA
|
||||
include M
|
||||
end
|
||||
|
||||
class B < BaseB
|
||||
include M
|
||||
end
|
||||
end
|
||||
|
||||
module MS3
|
||||
module ModA
|
||||
def foo(a)
|
||||
a << "ModA#foo"
|
||||
end
|
||||
def bar(a)
|
||||
a << "ModA#bar"
|
||||
foo(a)
|
||||
end
|
||||
end
|
||||
class A
|
||||
def foo(a)
|
||||
a << "A#foo"
|
||||
end
|
||||
class << self
|
||||
include ModA
|
||||
end
|
||||
end
|
||||
class B < A
|
||||
def self.foo(a)
|
||||
a << "B.foo"
|
||||
super(a)
|
||||
end
|
||||
def self.bar(a)
|
||||
a << "B.bar"
|
||||
super(a)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module MS4
|
||||
module Layer1
|
||||
def example
|
||||
5
|
||||
end
|
||||
end
|
||||
|
||||
module Layer2
|
||||
include Layer1
|
||||
def example
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
class A
|
||||
include Layer2
|
||||
public :example
|
||||
end
|
||||
end
|
||||
|
||||
class MM_A
|
||||
undef_method :is_a?
|
||||
end
|
||||
|
||||
class MM_B < MM_A
|
||||
def is_a?(blah)
|
||||
# should fire the method_missing below
|
||||
super
|
||||
end
|
||||
|
||||
def method_missing(*)
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
class Alias1
|
||||
def name
|
||||
[:alias1]
|
||||
end
|
||||
end
|
||||
|
||||
class Alias2 < Alias1
|
||||
def initialize
|
||||
@times = 0
|
||||
end
|
||||
|
||||
def name
|
||||
if @times >= 10
|
||||
raise "runaway super"
|
||||
end
|
||||
|
||||
@times += 1
|
||||
|
||||
# Use this so that we can see collect all supers that we see.
|
||||
# One bug that arises is that we call Alias2#name from Alias2#name
|
||||
# as it's superclass. In that case, either we get a runaway recursion
|
||||
# super OR we get the return value being [:alias2, :alias2, :alias1]
|
||||
# rather than [:alias2, :alias1].
|
||||
#
|
||||
# Which one depends on caches and how super is implemented.
|
||||
[:alias2] + super
|
||||
end
|
||||
end
|
||||
|
||||
class Alias3 < Alias2
|
||||
alias_method :name3, :name
|
||||
# In the method table for Alias3 now should be a special alias entry
|
||||
# that references Alias2 and Alias2#name (probably as an object).
|
||||
#
|
||||
# When name3 is called then, Alias2 (NOT Alias3) is presented as the
|
||||
# current module to Alias2#name, so that when super is called,
|
||||
# Alias2->superclass is next.
|
||||
#
|
||||
# Otherwise, Alias2 is next, which is where name was to begin with,
|
||||
# causing the wrong #name method to be called.
|
||||
end
|
||||
|
||||
module AliasWithSuper
|
||||
module AS1
|
||||
def foo
|
||||
:a
|
||||
end
|
||||
end
|
||||
|
||||
module BS1
|
||||
def foo
|
||||
[:b, super]
|
||||
end
|
||||
end
|
||||
|
||||
class Base
|
||||
extend AS1
|
||||
extend BS1
|
||||
end
|
||||
|
||||
class Trigger < Base
|
||||
class << self
|
||||
def foo_quux
|
||||
foo_baz
|
||||
end
|
||||
|
||||
alias_method :foo_baz, :foo
|
||||
alias_method :foo, :foo_quux
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module RestArgsWithSuper
|
||||
class A
|
||||
def a(*args)
|
||||
args
|
||||
end
|
||||
end
|
||||
|
||||
class B < A
|
||||
def a(*args)
|
||||
args << "foo"
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class AnonymousModuleIncludedTwiceBase
|
||||
def self.whatever
|
||||
mod = Module.new do
|
||||
def a(array)
|
||||
array << "anon"
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
include mod
|
||||
end
|
||||
|
||||
def a(array)
|
||||
array << "non-anon"
|
||||
end
|
||||
end
|
||||
|
||||
class AnonymousModuleIncludedTwice < AnonymousModuleIncludedTwiceBase
|
||||
whatever
|
||||
whatever
|
||||
end
|
||||
|
||||
module ZSuperWithBlock
|
||||
class A
|
||||
def a
|
||||
yield
|
||||
end
|
||||
|
||||
def b(&block)
|
||||
block.call
|
||||
end
|
||||
|
||||
def c
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
class B < A
|
||||
def a
|
||||
super { 14 }
|
||||
end
|
||||
|
||||
def b
|
||||
block_ref = lambda { 15 }
|
||||
[super { 14 }, super(&block_ref)]
|
||||
end
|
||||
|
||||
def c
|
||||
block_ref = lambda { 16 }
|
||||
super(&block_ref)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ZSuperWithOptional
|
||||
class A
|
||||
def m(x, y, z)
|
||||
z
|
||||
end
|
||||
end
|
||||
|
||||
class B < A
|
||||
def m(x, y, z = 14)
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
class C < A
|
||||
def m(x, y, z = 14)
|
||||
z = 100
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ZSuperWithRest
|
||||
class A
|
||||
def m(*args)
|
||||
args
|
||||
end
|
||||
|
||||
def m_modified(*args)
|
||||
args
|
||||
end
|
||||
end
|
||||
|
||||
class B < A
|
||||
def m(*args)
|
||||
super
|
||||
end
|
||||
|
||||
def m_modified(*args)
|
||||
args[1] = 14
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ZSuperWithRestAndOthers
|
||||
class A
|
||||
def m(a, b, *args)
|
||||
args
|
||||
end
|
||||
|
||||
def m_modified(a, b, *args)
|
||||
args
|
||||
end
|
||||
end
|
||||
|
||||
class B < A
|
||||
def m(a, b, *args)
|
||||
super
|
||||
end
|
||||
|
||||
def m_modified(a, b, *args)
|
||||
args[1] = 14
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ZSuperWithUnderscores
|
||||
class A
|
||||
def m(*args)
|
||||
args
|
||||
end
|
||||
|
||||
def m_modified(*args)
|
||||
args
|
||||
end
|
||||
end
|
||||
|
||||
class B < A
|
||||
def m(_, _)
|
||||
super
|
||||
end
|
||||
|
||||
def m_modified(_, _)
|
||||
_ = 14
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module KeywordArguments
|
||||
class A
|
||||
def foo(**args)
|
||||
args
|
||||
end
|
||||
end
|
||||
|
||||
class B < A
|
||||
def foo(**)
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
class C < A
|
||||
end
|
||||
end
|
||||
|
||||
module FromBasicObject
|
||||
def __send__(name, *args, &block)
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
module IntermediateBasic
|
||||
include FromBasicObject
|
||||
end
|
||||
|
||||
class IncludesFromBasic
|
||||
include FromBasicObject
|
||||
|
||||
def foobar; 43; end
|
||||
end
|
||||
|
||||
class IncludesIntermediate
|
||||
include IntermediateBasic
|
||||
|
||||
def foobar; 42; end
|
||||
end
|
||||
|
||||
module SingletonCase
|
||||
class Base
|
||||
def foobar(array)
|
||||
array << :base
|
||||
end
|
||||
end
|
||||
|
||||
class Foo < Base
|
||||
def foobar(array)
|
||||
array << :foo
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module SingletonAliasCase
|
||||
class Base
|
||||
def foobar(array)
|
||||
array << :base
|
||||
end
|
||||
|
||||
def alias_on_singleton
|
||||
object = self
|
||||
singleton = (class << object; self; end)
|
||||
singleton.__send__(:alias_method, :new_foobar, :foobar)
|
||||
end
|
||||
end
|
||||
|
||||
class Foo < Base
|
||||
def foobar(array)
|
||||
array << :foo
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module SplatAndKeyword
|
||||
class A
|
||||
def foo(*args, **options)
|
||||
[args, options]
|
||||
end
|
||||
end
|
||||
|
||||
class B < A
|
||||
def foo(*args, **options)
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
85
spec/ruby/language/fixtures/variables.rb
Normal file
85
spec/ruby/language/fixtures/variables.rb
Normal file
|
@ -0,0 +1,85 @@
|
|||
module VariablesSpecs
|
||||
class ParAsgn
|
||||
attr_accessor :x
|
||||
|
||||
def initialize
|
||||
@x = 0
|
||||
end
|
||||
|
||||
def inc
|
||||
@x += 1
|
||||
end
|
||||
|
||||
def to_ary
|
||||
[1,2,3,4]
|
||||
end
|
||||
end
|
||||
|
||||
class OpAsgn
|
||||
attr_accessor :a, :b, :side_effect
|
||||
|
||||
def do_side_effect
|
||||
self.side_effect = true
|
||||
return @a
|
||||
end
|
||||
|
||||
def do_more_side_effects
|
||||
@a += 5
|
||||
self
|
||||
end
|
||||
|
||||
def do_bool_side_effects
|
||||
@b += 1
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
class Hashalike
|
||||
def [](k) k end
|
||||
def []=(k, v) [k, v] end
|
||||
end
|
||||
|
||||
def self.reverse_foo(a, b)
|
||||
return b, a
|
||||
end
|
||||
|
||||
class ArrayLike
|
||||
def initialize(array)
|
||||
@array = array
|
||||
end
|
||||
|
||||
def to_a
|
||||
@array
|
||||
end
|
||||
end
|
||||
|
||||
class ArraySubclass < Array
|
||||
end
|
||||
|
||||
class PrivateMethods
|
||||
private
|
||||
|
||||
def to_ary
|
||||
[1, 2]
|
||||
end
|
||||
|
||||
def to_a
|
||||
[3, 4]
|
||||
end
|
||||
end
|
||||
|
||||
class ToAryNil
|
||||
def to_ary
|
||||
end
|
||||
end
|
||||
|
||||
class Chain
|
||||
def self.without_parenthesis a
|
||||
a
|
||||
end
|
||||
end
|
||||
|
||||
def self.false
|
||||
false
|
||||
end
|
||||
end
|
37
spec/ruby/language/fixtures/yield.rb
Normal file
37
spec/ruby/language/fixtures/yield.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
module YieldSpecs
|
||||
class Yielder
|
||||
def z
|
||||
yield
|
||||
end
|
||||
|
||||
def ze(&block)
|
||||
block = proc { block }
|
||||
yield
|
||||
end
|
||||
|
||||
def s(a)
|
||||
yield(a)
|
||||
end
|
||||
|
||||
def m(a, b, c)
|
||||
yield(a, b, c)
|
||||
end
|
||||
|
||||
def r(a)
|
||||
yield(*a)
|
||||
end
|
||||
|
||||
def rs(a, b, c)
|
||||
yield(a, b, *c)
|
||||
end
|
||||
|
||||
def self.define_deep(&inned_block)
|
||||
define_method 'deep' do |v|
|
||||
# should yield to inner_block
|
||||
yield v
|
||||
end
|
||||
end
|
||||
|
||||
define_deep { |v| v * 2}
|
||||
end
|
||||
end
|
177
spec/ruby/language/for_spec.rb
Normal file
177
spec/ruby/language/for_spec.rb
Normal file
|
@ -0,0 +1,177 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
# for name[, name]... in expr [do]
|
||||
# body
|
||||
# end
|
||||
describe "The for expression" do
|
||||
it "iterates over an Enumerable passing each element to the block" do
|
||||
j = 0
|
||||
for i in 1..3
|
||||
j += i
|
||||
end
|
||||
j.should == 6
|
||||
end
|
||||
|
||||
it "iterates over a list of arrays and destructures with empty comma" do
|
||||
for i, in [[1,2]]
|
||||
i.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
it "iterates over an Hash passing each key-value pair to the block" do
|
||||
k = 0
|
||||
l = 0
|
||||
|
||||
for i, j in { 1 => 10, 2 => 20 }
|
||||
k += i
|
||||
l += j
|
||||
end
|
||||
|
||||
k.should == 3
|
||||
l.should == 30
|
||||
end
|
||||
|
||||
it "iterates over any object responding to 'each'" do
|
||||
class XYZ
|
||||
def each
|
||||
(0..10).each { |i| yield i }
|
||||
end
|
||||
end
|
||||
|
||||
j = 0
|
||||
for i in XYZ.new
|
||||
j += i
|
||||
end
|
||||
j.should == 55
|
||||
end
|
||||
|
||||
it "allows an instance variable as an iterator name" do
|
||||
m = [1,2,3]
|
||||
n = 0
|
||||
for @var in m
|
||||
n += 1
|
||||
end
|
||||
@var.should == 3
|
||||
n.should == 3
|
||||
end
|
||||
|
||||
it "allows a class variable as an iterator name" do
|
||||
class OFor
|
||||
m = [1,2,3]
|
||||
n = 0
|
||||
for @@var in m
|
||||
n += 1
|
||||
end
|
||||
@@var.should == 3
|
||||
n.should == 3
|
||||
end
|
||||
end
|
||||
|
||||
it "allows a constant as an iterator name" do
|
||||
class OFor
|
||||
m = [1,2,3]
|
||||
n = 0
|
||||
-> {
|
||||
for CONST in m
|
||||
n += 1
|
||||
end
|
||||
}.should complain(/already initialized constant/)
|
||||
CONST.should == 3
|
||||
n.should == 3
|
||||
end
|
||||
end
|
||||
|
||||
# 1.9 behaviour verified by nobu in
|
||||
# http://redmine.ruby-lang.org/issues/show/2053
|
||||
it "yields only as many values as there are arguments" do
|
||||
class OFor
|
||||
def each
|
||||
[[1,2,3], [4,5,6]].each do |a|
|
||||
yield(a[0],a[1],a[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
o = OFor.new
|
||||
qs = []
|
||||
for q in o
|
||||
qs << q
|
||||
end
|
||||
qs.should == [1, 4]
|
||||
q.should == 4
|
||||
end
|
||||
|
||||
it "optionally takes a 'do' after the expression" do
|
||||
j = 0
|
||||
for i in 1..3 do
|
||||
j += i
|
||||
end
|
||||
j.should == 6
|
||||
end
|
||||
|
||||
it "allows body begin on the same line if do is used" do
|
||||
j = 0
|
||||
for i in 1..3 do j += i
|
||||
end
|
||||
j.should == 6
|
||||
end
|
||||
|
||||
it "executes code in containing variable scope" do
|
||||
for i in 1..2
|
||||
a = 123
|
||||
end
|
||||
|
||||
a.should == 123
|
||||
end
|
||||
|
||||
it "executes code in containing variable scope with 'do'" do
|
||||
for i in 1..2 do
|
||||
a = 123
|
||||
end
|
||||
|
||||
a.should == 123
|
||||
end
|
||||
|
||||
it "returns expr" do
|
||||
for i in 1..3; end.should == (1..3)
|
||||
for i,j in { 1 => 10, 2 => 20 }; end.should == { 1 => 10, 2 => 20 }
|
||||
end
|
||||
|
||||
it "breaks out of a loop upon 'break', returning nil" do
|
||||
j = 0
|
||||
for i in 1..3
|
||||
j += i
|
||||
|
||||
break if i == 2
|
||||
end.should == nil
|
||||
|
||||
j.should == 3
|
||||
end
|
||||
|
||||
it "allows 'break' to have an argument which becomes the value of the for expression" do
|
||||
for i in 1..3
|
||||
break 10 if i == 2
|
||||
end.should == 10
|
||||
end
|
||||
|
||||
it "starts the next iteration with 'next'" do
|
||||
j = 0
|
||||
for i in 1..5
|
||||
next if i == 2
|
||||
|
||||
j += i
|
||||
end
|
||||
|
||||
j.should == 13
|
||||
end
|
||||
|
||||
it "repeats current iteration with 'redo'" do
|
||||
j = 0
|
||||
for i in 1..3
|
||||
j += i
|
||||
|
||||
redo if i == 2 && j < 4
|
||||
end
|
||||
|
||||
j.should == 8
|
||||
end
|
||||
end
|
154
spec/ruby/language/hash_spec.rb
Normal file
154
spec/ruby/language/hash_spec.rb
Normal file
|
@ -0,0 +1,154 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/hash_strings_ascii8bit', __FILE__)
|
||||
require File.expand_path('../fixtures/hash_strings_utf8', __FILE__)
|
||||
require File.expand_path('../fixtures/hash_strings_usascii', __FILE__)
|
||||
|
||||
describe "Hash literal" do
|
||||
it "{} should return an empty hash" do
|
||||
{}.size.should == 0
|
||||
{}.should == {}
|
||||
end
|
||||
|
||||
it "{} should return a new hash populated with the given elements" do
|
||||
h = {a: 'a', 'b' => 3, 44 => 2.3}
|
||||
h.size.should == 3
|
||||
h.should == {a: "a", "b" => 3, 44 => 2.3}
|
||||
end
|
||||
|
||||
it "treats empty expressions as nils" do
|
||||
h = {() => ()}
|
||||
h.keys.should == [nil]
|
||||
h.values.should == [nil]
|
||||
h[nil].should == nil
|
||||
|
||||
h = {() => :value}
|
||||
h.keys.should == [nil]
|
||||
h.values.should == [:value]
|
||||
h[nil].should == :value
|
||||
|
||||
h = {key: ()}
|
||||
h.keys.should == [:key]
|
||||
h.values.should == [nil]
|
||||
h[:key].should == nil
|
||||
end
|
||||
|
||||
it "freezes string keys on initialization" do
|
||||
key = "foo"
|
||||
h = {key => "bar"}
|
||||
key.reverse!
|
||||
h["foo"].should == "bar"
|
||||
h.keys.first.should == "foo"
|
||||
h.keys.first.frozen?.should == true
|
||||
key.should == "oof"
|
||||
end
|
||||
|
||||
it "checks duplicated keys on initialization" do
|
||||
-> {
|
||||
@h = eval "{foo: :bar, foo: :foo}"
|
||||
}.should complain(/key :foo is duplicated|duplicated key/)
|
||||
@h.keys.size.should == 1
|
||||
@h.should == {foo: :foo}
|
||||
end
|
||||
|
||||
it "accepts a hanging comma" do
|
||||
h = {a: 1, b: 2,}
|
||||
h.size.should == 2
|
||||
h.should == {a: 1, b: 2}
|
||||
end
|
||||
|
||||
it "recognizes '=' at the end of the key" do
|
||||
eval("{:a==>1}").should == {:"a=" => 1}
|
||||
eval("{:a= =>1}").should == {:"a=" => 1}
|
||||
eval("{:a= => 1}").should == {:"a=" => 1}
|
||||
end
|
||||
|
||||
it "with '==>' in the middle raises SyntaxError" do
|
||||
lambda { eval("{:a ==> 1}") }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "constructs a new hash with the given elements" do
|
||||
{foo: 123}.should == {foo: 123}
|
||||
h = {rbx: :cool, specs: 'fail_sometimes'}
|
||||
{rbx: :cool, specs: 'fail_sometimes'}.should == h
|
||||
end
|
||||
|
||||
it "ignores a hanging comma" do
|
||||
{foo: 123,}.should == {foo: 123}
|
||||
h = {rbx: :cool, specs: 'fail_sometimes'}
|
||||
{rbx: :cool, specs: 'fail_sometimes',}.should == h
|
||||
end
|
||||
|
||||
it "accepts mixed 'key: value' and 'key => value' syntax" do
|
||||
h = {:a => 1, :b => 2, "c" => 3}
|
||||
{a: 1, b: 2, "c" => 3}.should == h
|
||||
end
|
||||
|
||||
it "accepts mixed 'key: value', 'key => value' and '\"key\"': value' syntax" do
|
||||
h = {:a => 1, :b => 2, "c" => 3, :d => 4}
|
||||
eval('{a: 1, :b => 2, "c" => 3, "d": 4}').should == h
|
||||
end
|
||||
|
||||
it "expands an '**{}' element into the containing Hash literal initialization" do
|
||||
{a: 1, **{b: 2}, c: 3}.should == {a: 1, b: 2, c: 3}
|
||||
end
|
||||
|
||||
it "expands an '**obj' element into the containing Hash literal initialization" do
|
||||
h = {b: 2, c: 3}
|
||||
{**h, a: 1}.should == {b: 2, c: 3, a: 1}
|
||||
{a: 1, **h}.should == {a: 1, b: 2, c: 3}
|
||||
{a: 1, **h, c: 4}.should == {a: 1, b: 2, c: 4}
|
||||
end
|
||||
|
||||
it "expands a BasicObject using ** into the containing Hash literal initialization" do
|
||||
h = BasicObject.new
|
||||
def h.to_hash; {:b => 2, :c => 3}; end
|
||||
{**h, a: 1}.should == {b: 2, c: 3, a: 1}
|
||||
{a: 1, **h}.should == {a: 1, b: 2, c: 3}
|
||||
{a: 1, **h, c: 4}.should == {a: 1, b: 2, c: 4}
|
||||
end
|
||||
|
||||
it "expands an '**{}' element with the last key/value pair taking precedence" do
|
||||
-> {
|
||||
@h = eval "{a: 1, **{a: 2, b: 3, c: 1}, c: 3}"
|
||||
}.should complain(/key :a is duplicated|duplicated key/)
|
||||
@h.should == {a: 2, b: 3, c: 3}
|
||||
end
|
||||
|
||||
it "merges multiple nested '**obj' in Hash literals" do
|
||||
-> {
|
||||
@h = eval "{a: 1, **{a: 2, **{b: 3, **{c: 4}}, **{d: 5}, }, **{d: 6}}"
|
||||
}.should complain(/key :a is duplicated|duplicated key/)
|
||||
@h.should == {a: 2, b: 3, c: 4, d: 6}
|
||||
end
|
||||
|
||||
it "calls #to_hash to expand an '**obj' element" do
|
||||
obj = mock("hash splat")
|
||||
obj.should_receive(:to_hash).and_return({b: 2, d: 4})
|
||||
|
||||
{a: 1, **obj, c: 3}.should == {a:1, b: 2, c: 3, d: 4}
|
||||
end
|
||||
|
||||
it "raises a TypeError if any splatted elements keys are not symbols" do
|
||||
h = {1 => 2, b: 3}
|
||||
lambda { {a: 1, **h} }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises a TypeError if #to_hash does not return a Hash" do
|
||||
obj = mock("hash splat")
|
||||
obj.should_receive(:to_hash).and_return(obj)
|
||||
|
||||
lambda { {**obj} }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "does not change encoding of literal string keys during creation" do
|
||||
ascii8bit_hash = HashStringsASCII8BIT.literal_hash
|
||||
utf8_hash = HashStringsUTF8.literal_hash
|
||||
usascii_hash = HashStringsUSASCII.literal_hash
|
||||
|
||||
ascii8bit_hash.keys.first.encoding.should == Encoding::ASCII_8BIT
|
||||
ascii8bit_hash.keys.first.should == utf8_hash.keys.first
|
||||
utf8_hash.keys.first.encoding.should == Encoding::UTF_8
|
||||
utf8_hash.keys.first.should == usascii_hash.keys.first
|
||||
usascii_hash.keys.first.encoding.should == Encoding::US_ASCII
|
||||
end
|
||||
end
|
87
spec/ruby/language/heredoc_spec.rb
Normal file
87
spec/ruby/language/heredoc_spec.rb
Normal file
|
@ -0,0 +1,87 @@
|
|||
# -*- encoding: us-ascii -*-
|
||||
|
||||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "Heredoc string" do
|
||||
|
||||
before :each do
|
||||
@ip = 'xxx' # used for interpolation
|
||||
end
|
||||
|
||||
it "allows HEREDOC with <<identifier, interpolated" do
|
||||
s = <<HERE
|
||||
foo bar#{@ip}
|
||||
HERE
|
||||
s.should == "foo barxxx\n"
|
||||
end
|
||||
|
||||
it 'allow HEREDOC with <<"identifier", interpolated' do
|
||||
s = <<"HERE"
|
||||
foo bar#{@ip}
|
||||
HERE
|
||||
s.should == "foo barxxx\n"
|
||||
end
|
||||
|
||||
it "allows HEREDOC with <<'identifier', no interpolation" do
|
||||
s = <<'HERE'
|
||||
foo bar#{@ip}
|
||||
HERE
|
||||
s.should == 'foo bar#{@ip}' + "\n"
|
||||
end
|
||||
|
||||
it "allows HEREDOC with <<-identifier, allowing to indent identifier, interpolated" do
|
||||
s = <<-HERE
|
||||
foo bar#{@ip}
|
||||
HERE
|
||||
|
||||
s.should == " foo barxxx\n"
|
||||
end
|
||||
|
||||
it 'allows HEREDOC with <<-"identifier", allowing to indent identifier, interpolated' do
|
||||
s = <<-"HERE"
|
||||
foo bar#{@ip}
|
||||
HERE
|
||||
|
||||
s.should == " foo barxxx\n"
|
||||
end
|
||||
|
||||
it "allows HEREDOC with <<-'identifier', allowing to indent identifier, no interpolation" do
|
||||
s = <<-'HERE'
|
||||
foo bar#{@ip}
|
||||
HERE
|
||||
|
||||
s.should == ' foo bar#{@ip}' + "\n"
|
||||
end
|
||||
|
||||
ruby_version_is "2.3" do
|
||||
it "allows HEREDOC with <<~'identifier', allowing to indent identifier and content" do
|
||||
require File.expand_path('../fixtures/squiggly_heredoc', __FILE__)
|
||||
SquigglyHeredocSpecs.message.should == "character density, n.:\n The number of very weird people in the office.\n"
|
||||
end
|
||||
|
||||
it "trims trailing newline character for blank HEREDOC with <<~'identifier'" do
|
||||
require File.expand_path('../fixtures/squiggly_heredoc', __FILE__)
|
||||
SquigglyHeredocSpecs.blank.should == ""
|
||||
end
|
||||
|
||||
it 'allows HEREDOC with <<~identifier, interpolated' do
|
||||
require File.expand_path('../fixtures/squiggly_heredoc', __FILE__)
|
||||
SquigglyHeredocSpecs.unquoted.should == "unquoted interpolated\n"
|
||||
end
|
||||
|
||||
it 'allows HEREDOC with <<~"identifier", interpolated' do
|
||||
require File.expand_path('../fixtures/squiggly_heredoc', __FILE__)
|
||||
SquigglyHeredocSpecs.doublequoted.should == "doublequoted interpolated\n"
|
||||
end
|
||||
|
||||
it "allows HEREDOC with <<~'identifier', no interpolation" do
|
||||
require File.expand_path('../fixtures/squiggly_heredoc', __FILE__)
|
||||
SquigglyHeredocSpecs.singlequoted.should == "singlequoted \#{\"interpolated\"}\n"
|
||||
end
|
||||
|
||||
it "selects the least-indented line and removes its indentation from all the lines" do
|
||||
require File.expand_path('../fixtures/squiggly_heredoc', __FILE__)
|
||||
SquigglyHeredocSpecs.least_indented_on_the_last_line.should == " a\n b\nc\n"
|
||||
end
|
||||
end
|
||||
end
|
354
spec/ruby/language/if_spec.rb
Normal file
354
spec/ruby/language/if_spec.rb
Normal file
|
@ -0,0 +1,354 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "The if expression" do
|
||||
it "evaluates body if expression is true" do
|
||||
a = []
|
||||
if true
|
||||
a << 123
|
||||
end
|
||||
a.should == [123]
|
||||
end
|
||||
|
||||
it "does not evaluate body if expression is false" do
|
||||
a = []
|
||||
if false
|
||||
a << 123
|
||||
end
|
||||
a.should == []
|
||||
end
|
||||
|
||||
it "does not evaluate body if expression is empty" do
|
||||
a = []
|
||||
if ()
|
||||
a << 123
|
||||
end
|
||||
a.should == []
|
||||
end
|
||||
|
||||
it "does not evaluate else-body if expression is true" do
|
||||
a = []
|
||||
if true
|
||||
a << 123
|
||||
else
|
||||
a << 456
|
||||
end
|
||||
a.should == [123]
|
||||
end
|
||||
|
||||
it "evaluates only else-body if expression is false" do
|
||||
a = []
|
||||
if false
|
||||
a << 123
|
||||
else
|
||||
a << 456
|
||||
end
|
||||
a.should == [456]
|
||||
end
|
||||
|
||||
it "returns result of then-body evaluation if expression is true" do
|
||||
if true
|
||||
123
|
||||
end.should == 123
|
||||
end
|
||||
|
||||
it "returns result of last statement in then-body if expression is true" do
|
||||
if true
|
||||
'foo'
|
||||
'bar'
|
||||
'baz'
|
||||
end.should == 'baz'
|
||||
end
|
||||
|
||||
it "returns result of then-body evaluation if expression is true and else part is present" do
|
||||
if true
|
||||
123
|
||||
else
|
||||
456
|
||||
end.should == 123
|
||||
end
|
||||
|
||||
it "returns result of else-body evaluation if expression is false" do
|
||||
if false
|
||||
123
|
||||
else
|
||||
456
|
||||
end.should == 456
|
||||
end
|
||||
|
||||
it "returns nil if then-body is empty and expression is true" do
|
||||
if true
|
||||
end.should == nil
|
||||
end
|
||||
|
||||
it "returns nil if then-body is empty, expression is true and else part is present" do
|
||||
if true
|
||||
else
|
||||
456
|
||||
end.should == nil
|
||||
end
|
||||
|
||||
it "returns nil if then-body is empty, expression is true and else part is empty" do
|
||||
if true
|
||||
else
|
||||
end.should == nil
|
||||
end
|
||||
|
||||
it "returns nil if else-body is empty and expression is false" do
|
||||
if false
|
||||
123
|
||||
else
|
||||
end.should == nil
|
||||
end
|
||||
|
||||
it "returns nil if else-body is empty, expression is false and then-body is empty" do
|
||||
if false
|
||||
else
|
||||
end.should == nil
|
||||
end
|
||||
|
||||
it "considers an expression with nil result as false" do
|
||||
if nil
|
||||
123
|
||||
else
|
||||
456
|
||||
end.should == 456
|
||||
end
|
||||
|
||||
it "considers a non-nil and non-boolean object in expression result as true" do
|
||||
if mock('x')
|
||||
123
|
||||
else
|
||||
456
|
||||
end.should == 123
|
||||
end
|
||||
|
||||
it "considers a zero integer in expression result as true" do
|
||||
if 0
|
||||
123
|
||||
else
|
||||
456
|
||||
end.should == 123
|
||||
end
|
||||
|
||||
it "allows starting else-body on the same line" do
|
||||
if false
|
||||
123
|
||||
else 456
|
||||
end.should == 456
|
||||
end
|
||||
|
||||
it "evaluates subsequent elsif statements and execute body of first matching" do
|
||||
if false
|
||||
123
|
||||
elsif false
|
||||
234
|
||||
elsif true
|
||||
345
|
||||
elsif true
|
||||
456
|
||||
end.should == 345
|
||||
end
|
||||
|
||||
it "evaluates else-body if no if/elsif statements match" do
|
||||
if false
|
||||
123
|
||||
elsif false
|
||||
234
|
||||
elsif false
|
||||
345
|
||||
else
|
||||
456
|
||||
end.should == 456
|
||||
end
|
||||
|
||||
it "allows 'then' after expression when then-body is on the next line" do
|
||||
if true then
|
||||
123
|
||||
end.should == 123
|
||||
|
||||
if true then ; 123; end.should == 123
|
||||
end
|
||||
|
||||
it "allows then-body on the same line separated with 'then'" do
|
||||
if true then 123
|
||||
end.should == 123
|
||||
|
||||
if true then 123; end.should == 123
|
||||
end
|
||||
|
||||
it "returns nil when then-body on the same line separated with 'then' and expression is false" do
|
||||
if false then 123
|
||||
end.should == nil
|
||||
|
||||
if false then 123; end.should == nil
|
||||
end
|
||||
|
||||
it "returns nil when then-body separated by 'then' is empty and expression is true" do
|
||||
if true then
|
||||
end.should == nil
|
||||
|
||||
if true then ; end.should == nil
|
||||
end
|
||||
|
||||
it "returns nil when then-body separated by 'then', expression is false and no else part" do
|
||||
if false then
|
||||
end.should == nil
|
||||
|
||||
if false then ; end.should == nil
|
||||
end
|
||||
|
||||
it "evaluates then-body when then-body separated by 'then', expression is true and else part is present" do
|
||||
if true then 123
|
||||
else 456
|
||||
end.should == 123
|
||||
|
||||
if true then 123; else 456; end.should == 123
|
||||
end
|
||||
|
||||
it "evaluates else-body when then-body separated by 'then' and expression is false" do
|
||||
if false then 123
|
||||
else 456
|
||||
end.should == 456
|
||||
|
||||
if false then 123; else 456; end.should == 456
|
||||
end
|
||||
|
||||
describe "with a boolean range ('flip-flop' operator)" do
|
||||
before :each do
|
||||
ScratchPad.record []
|
||||
end
|
||||
|
||||
after :each do
|
||||
ScratchPad.clear
|
||||
end
|
||||
|
||||
it "mimics an awk conditional with a single-element inclusive-end range" do
|
||||
10.times { |i| ScratchPad << i if (i == 4)..(i == 4) }
|
||||
ScratchPad.recorded.should == [4]
|
||||
end
|
||||
|
||||
it "mimics an awk conditional with a many-element inclusive-end range" do
|
||||
10.times { |i| ScratchPad << i if (i == 4)..(i == 7) }
|
||||
ScratchPad.recorded.should == [4, 5, 6, 7]
|
||||
end
|
||||
|
||||
it "mimics a sed conditional with a zero-element exclusive-end range" do
|
||||
eval "10.times { |i| ScratchPad << i if (i == 4)...(i == 4) }"
|
||||
ScratchPad.recorded.should == [4, 5, 6, 7, 8, 9]
|
||||
end
|
||||
|
||||
it "mimics a sed conditional with a many-element exclusive-end range" do
|
||||
10.times { |i| ScratchPad << i if (i == 4)...(i == 5) }
|
||||
ScratchPad.recorded.should == [4, 5]
|
||||
end
|
||||
|
||||
it "allows combining two flip-flops" do
|
||||
10.times { |i| ScratchPad << i if (i == 4)...(i == 5) or (i == 7)...(i == 8) }
|
||||
ScratchPad.recorded.should == [4, 5, 7, 8]
|
||||
end
|
||||
|
||||
it "evaluates the first conditions lazily with inclusive-end range" do
|
||||
collector = proc { |i| ScratchPad << i }
|
||||
eval "10.times { |i| i if collector[i]...false }"
|
||||
ScratchPad.recorded.should == [0]
|
||||
end
|
||||
|
||||
it "evaluates the first conditions lazily with exclusive-end range" do
|
||||
collector = proc { |i| ScratchPad << i }
|
||||
eval "10.times { |i| i if collector[i]..false }"
|
||||
ScratchPad.recorded.should == [0]
|
||||
end
|
||||
|
||||
it "evaluates the second conditions lazily with inclusive-end range" do
|
||||
collector = proc { |i| ScratchPad << i }
|
||||
10.times { |i| i if (i == 4)...collector[i] }
|
||||
ScratchPad.recorded.should == [5]
|
||||
end
|
||||
|
||||
it "evaluates the second conditions lazily with exclusive-end range" do
|
||||
collector = proc { |i| ScratchPad << i }
|
||||
10.times { |i| i if (i == 4)..collector[i] }
|
||||
ScratchPad.recorded.should == [4]
|
||||
end
|
||||
|
||||
it "scopes state by flip-flop" do
|
||||
store_me = proc { |i| ScratchPad << i if (i == 4)..(i == 7) }
|
||||
store_me[1]
|
||||
store_me[4]
|
||||
proc { store_me[1] }.call
|
||||
store_me[7]
|
||||
store_me[5]
|
||||
ScratchPad.recorded.should == [4, 1, 7]
|
||||
end
|
||||
|
||||
it "keeps flip-flops from interfering" do
|
||||
a = eval "proc { |i| ScratchPad << i if (i == 4)..(i == 7) }"
|
||||
b = eval "proc { |i| ScratchPad << i if (i == 4)..(i == 7) }"
|
||||
6.times(&a)
|
||||
6.times(&b)
|
||||
ScratchPad.recorded.should == [4, 5, 4, 5]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "The postfix if form" do
|
||||
it "evaluates statement if expression is true" do
|
||||
a = []
|
||||
a << 123 if true
|
||||
a.should == [123]
|
||||
end
|
||||
|
||||
it "does not evaluate statement if expression is false" do
|
||||
a = []
|
||||
a << 123 if false
|
||||
a.should == []
|
||||
end
|
||||
|
||||
it "returns result of expression if value is true" do
|
||||
(123 if true).should == 123
|
||||
end
|
||||
|
||||
it "returns nil if expression is false" do
|
||||
(123 if false).should == nil
|
||||
end
|
||||
|
||||
it "considers a nil expression as false" do
|
||||
(123 if nil).should == nil
|
||||
end
|
||||
|
||||
it "considers a non-nil object as true" do
|
||||
(123 if mock('x')).should == 123
|
||||
end
|
||||
|
||||
it "evaluates then-body in containing scope" do
|
||||
a = 123
|
||||
if true
|
||||
b = a+1
|
||||
end
|
||||
b.should == 124
|
||||
end
|
||||
|
||||
it "evaluates else-body in containing scope" do
|
||||
a = 123
|
||||
if false
|
||||
b = a+1
|
||||
else
|
||||
b = a+2
|
||||
end
|
||||
b.should == 125
|
||||
end
|
||||
|
||||
it "evaluates elsif-body in containing scope" do
|
||||
a = 123
|
||||
if false
|
||||
b = a+1
|
||||
elsif false
|
||||
b = a+2
|
||||
elsif true
|
||||
b = a+3
|
||||
else
|
||||
b = a+4
|
||||
end
|
||||
b.should == 126
|
||||
end
|
||||
end
|
573
spec/ruby/language/lambda_spec.rb
Normal file
573
spec/ruby/language/lambda_spec.rb
Normal file
|
@ -0,0 +1,573 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "A lambda literal -> () { }" do
|
||||
SpecEvaluate.desc = "for definition"
|
||||
|
||||
it "returns a Proc object when used in a BasicObject method" do
|
||||
klass = Class.new(BasicObject) do
|
||||
def create_lambda
|
||||
-> () { }
|
||||
end
|
||||
end
|
||||
|
||||
klass.new.create_lambda.should be_an_instance_of(Proc)
|
||||
end
|
||||
|
||||
it "does not execute the block" do
|
||||
->() { fail }.should be_an_instance_of(Proc)
|
||||
end
|
||||
|
||||
it "returns a lambda" do
|
||||
-> () { }.lambda?.should be_true
|
||||
end
|
||||
|
||||
it "has its own scope for local variables" do
|
||||
l = -> arg {
|
||||
var = arg
|
||||
# this would override var if it was declared outside the lambda
|
||||
l.call(arg-1) if arg > 0
|
||||
var
|
||||
}
|
||||
l.call(1).should == 1
|
||||
end
|
||||
|
||||
context "assigns no local variables" do
|
||||
evaluate <<-ruby do
|
||||
@a = -> { }
|
||||
@b = ->() { }
|
||||
@c = -> () { }
|
||||
@d = -> do end
|
||||
ruby
|
||||
|
||||
@a.().should be_nil
|
||||
@b.().should be_nil
|
||||
@c.().should be_nil
|
||||
@d.().should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "assigns variables from parameters" do
|
||||
evaluate <<-ruby do
|
||||
@a = -> (a) { a }
|
||||
ruby
|
||||
|
||||
@a.(1).should == 1
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> ((a)) { a }
|
||||
ruby
|
||||
|
||||
@a.(1).should == 1
|
||||
@a.([1, 2, 3]).should == 1
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> ((*a, b)) { [a, b] }
|
||||
ruby
|
||||
|
||||
@a.(1).should == [[], 1]
|
||||
@a.([1, 2, 3]).should == [[1, 2], 3]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (a={}) { a }
|
||||
ruby
|
||||
|
||||
@a.().should == {}
|
||||
@a.(2).should == 2
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (*) { }
|
||||
ruby
|
||||
|
||||
@a.().should be_nil
|
||||
@a.(1).should be_nil
|
||||
@a.(1, 2, 3).should be_nil
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (*a) { a }
|
||||
ruby
|
||||
|
||||
@a.().should == []
|
||||
@a.(1).should == [1]
|
||||
@a.(1, 2, 3).should == [1, 2, 3]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (a:) { a }
|
||||
ruby
|
||||
|
||||
lambda { @a.() }.should raise_error(ArgumentError)
|
||||
@a.(a: 1).should == 1
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (a: 1) { a }
|
||||
ruby
|
||||
|
||||
@a.().should == 1
|
||||
@a.(a: 2).should == 2
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (**) { }
|
||||
ruby
|
||||
|
||||
@a.().should be_nil
|
||||
@a.(a: 1, b: 2).should be_nil
|
||||
lambda { @a.(1) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (**k) { k }
|
||||
ruby
|
||||
|
||||
@a.().should == {}
|
||||
@a.(a: 1, b: 2).should == {a: 1, b: 2}
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (&b) { b }
|
||||
ruby
|
||||
|
||||
@a.().should be_nil
|
||||
@a.() { }.should be_an_instance_of(Proc)
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (a, b) { [a, b] }
|
||||
ruby
|
||||
|
||||
@a.(1, 2).should == [1, 2]
|
||||
lambda { @a.() }.should raise_error(ArgumentError)
|
||||
lambda { @a.(1) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> ((a, b, *c, d), (*e, f, g), (*h)) do
|
||||
[a, b, c, d, e, f, g, h]
|
||||
end
|
||||
ruby
|
||||
|
||||
@a.(1, 2, 3).should == [1, nil, [], nil, [], 2, nil, [3]]
|
||||
result = @a.([1, 2, 3], [4, 5, 6, 7, 8], [9, 10])
|
||||
result.should == [1, 2, [], 3, [4, 5, 6], 7, 8, [9, 10]]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (a, (b, (c, *d, (e, (*f)), g), (h, (i, j)))) do
|
||||
[a, b, c, d, e, f, g, h, i, j]
|
||||
end
|
||||
ruby
|
||||
|
||||
@a.(1, 2).should == [1, 2, nil, [], nil, [nil], nil, nil, nil, nil]
|
||||
result = @a.(1, [2, [3, 4, 5, [6, [7, 8]], 9], [10, [11, 12]]])
|
||||
result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (*, **k) { k }
|
||||
ruby
|
||||
|
||||
@a.().should == {}
|
||||
@a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
|
||||
|
||||
h = mock("keyword splat")
|
||||
h.should_receive(:to_hash).and_return({a: 1})
|
||||
@a.(h).should == {a: 1}
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (*, &b) { b }
|
||||
ruby
|
||||
|
||||
@a.().should be_nil
|
||||
@a.(1, 2, 3, 4).should be_nil
|
||||
@a.(&(l = ->{})).should equal(l)
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (a:, b:) { [a, b] }
|
||||
ruby
|
||||
|
||||
@a.(a: 1, b: 2).should == [1, 2]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (a:, b: 1) { [a, b] }
|
||||
ruby
|
||||
|
||||
@a.(a: 1).should == [1, 1]
|
||||
@a.(a: 1, b: 2).should == [1, 2]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (a: 1, b:) { [a, b] }
|
||||
ruby
|
||||
|
||||
@a.(b: 0).should == [1, 0]
|
||||
@a.(b: 2, a: 3).should == [3, 2]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (a: @a = -> (a: 1) { a }, b:) do
|
||||
[a, b]
|
||||
end
|
||||
ruby
|
||||
|
||||
@a.(a: 2, b: 3).should == [2, 3]
|
||||
@a.(b: 1).should == [@a, 1]
|
||||
|
||||
# Note the default value of a: in the original method.
|
||||
@a.().should == 1
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (a: 1, b: 2) { [a, b] }
|
||||
ruby
|
||||
|
||||
@a.().should == [1, 2]
|
||||
@a.(b: 3, a: 4).should == [4, 3]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> (a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l) do
|
||||
[a, b, c, d, e, f, g, h, k, l]
|
||||
end
|
||||
ruby
|
||||
|
||||
result = @a.(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{}))
|
||||
result.should == [9, 8, [7], [], 6, 5, 4, 3, {}, l]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = -> a, b=1, *c, d, e:, f: 2, g:, **k, &l do
|
||||
[a, b, c, d, e, f, g, k, l]
|
||||
end
|
||||
ruby
|
||||
|
||||
result = @a.(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{}))
|
||||
result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l]
|
||||
end
|
||||
|
||||
describe "with circular optional argument reference" do
|
||||
it "shadows an existing local with the same name as the argument" do
|
||||
a = 1
|
||||
-> {
|
||||
@proc = eval "-> (a=a) { a }"
|
||||
}.should complain(/circular argument reference/)
|
||||
@proc.call.should == nil
|
||||
end
|
||||
|
||||
it "shadows an existing method with the same name as the argument" do
|
||||
def a; 1; end
|
||||
-> {
|
||||
@proc = eval "-> (a=a) { a }"
|
||||
}.should complain(/circular argument reference/)
|
||||
@proc.call.should == nil
|
||||
end
|
||||
|
||||
it "calls an existing method with the same name as the argument if explicitly using ()" do
|
||||
def a; 1; end
|
||||
-> (a=a()) { a }.call.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "A lambda expression 'lambda { ... }'" do
|
||||
SpecEvaluate.desc = "for definition"
|
||||
|
||||
it "calls the #lambda method" do
|
||||
obj = mock("lambda definition")
|
||||
obj.should_receive(:lambda).and_return(obj)
|
||||
|
||||
def obj.define
|
||||
lambda { }
|
||||
end
|
||||
|
||||
obj.define.should equal(obj)
|
||||
end
|
||||
|
||||
it "does not execute the block" do
|
||||
lambda { fail }.should be_an_instance_of(Proc)
|
||||
end
|
||||
|
||||
it "returns a lambda" do
|
||||
lambda { }.lambda?.should be_true
|
||||
end
|
||||
|
||||
it "requires a block" do
|
||||
lambda { lambda }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
context "with an implicit block" do
|
||||
before do
|
||||
def meth; lambda; end
|
||||
end
|
||||
|
||||
it "can be created" do
|
||||
implicit_lambda = nil
|
||||
-> {
|
||||
implicit_lambda = meth { 1 }
|
||||
}.should complain(/tried to create Proc object without a block/)
|
||||
|
||||
implicit_lambda.lambda?.should be_true
|
||||
implicit_lambda.call.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
context "assigns no local variables" do
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { }
|
||||
@b = lambda { || }
|
||||
ruby
|
||||
|
||||
@a.().should be_nil
|
||||
@b.().should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "assigns variables from parameters" do
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |a| a }
|
||||
ruby
|
||||
|
||||
@a.(1).should == 1
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
def m(*a) yield(*a) end
|
||||
@a = lambda { |a| a }
|
||||
ruby
|
||||
|
||||
lambda { m(&@a) }.should raise_error(ArgumentError)
|
||||
lambda { m(1, 2, &@a) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |a, | a }
|
||||
ruby
|
||||
|
||||
@a.(1).should == 1
|
||||
@a.([1, 2]).should == [1, 2]
|
||||
|
||||
lambda { @a.() }.should raise_error(ArgumentError)
|
||||
lambda { @a.(1, 2) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
def m(a) yield a end
|
||||
def m2() yield end
|
||||
|
||||
@a = lambda { |a, | a }
|
||||
ruby
|
||||
|
||||
m(1, &@a).should == 1
|
||||
m([1, 2], &@a).should == [1, 2]
|
||||
|
||||
lambda { m2(&@a) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |(a)| a }
|
||||
ruby
|
||||
|
||||
@a.(1).should == 1
|
||||
@a.([1, 2, 3]).should == 1
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |(*a, b)| [a, b] }
|
||||
ruby
|
||||
|
||||
@a.(1).should == [[], 1]
|
||||
@a.([1, 2, 3]).should == [[1, 2], 3]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |a={}| a }
|
||||
ruby
|
||||
|
||||
@a.().should == {}
|
||||
@a.(2).should == 2
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |*| }
|
||||
ruby
|
||||
|
||||
@a.().should be_nil
|
||||
@a.(1).should be_nil
|
||||
@a.(1, 2, 3).should be_nil
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |*a| a }
|
||||
ruby
|
||||
|
||||
@a.().should == []
|
||||
@a.(1).should == [1]
|
||||
@a.(1, 2, 3).should == [1, 2, 3]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |a:| a }
|
||||
ruby
|
||||
|
||||
lambda { @a.() }.should raise_error(ArgumentError)
|
||||
@a.(a: 1).should == 1
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |a: 1| a }
|
||||
ruby
|
||||
|
||||
@a.().should == 1
|
||||
@a.(a: 2).should == 2
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |**| }
|
||||
ruby
|
||||
|
||||
@a.().should be_nil
|
||||
@a.(a: 1, b: 2).should be_nil
|
||||
lambda { @a.(1) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |**k| k }
|
||||
ruby
|
||||
|
||||
@a.().should == {}
|
||||
@a.(a: 1, b: 2).should == {a: 1, b: 2}
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |&b| b }
|
||||
ruby
|
||||
|
||||
@a.().should be_nil
|
||||
@a.() { }.should be_an_instance_of(Proc)
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |a, b| [a, b] }
|
||||
ruby
|
||||
|
||||
@a.(1, 2).should == [1, 2]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda do |(a, b, *c, d), (*e, f, g), (*h)|
|
||||
[a, b, c, d, e, f, g, h]
|
||||
end
|
||||
ruby
|
||||
|
||||
@a.(1, 2, 3).should == [1, nil, [], nil, [], 2, nil, [3]]
|
||||
result = @a.([1, 2, 3], [4, 5, 6, 7, 8], [9, 10])
|
||||
result.should == [1, 2, [], 3, [4, 5, 6], 7, 8, [9, 10]]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda do |a, (b, (c, *d, (e, (*f)), g), (h, (i, j)))|
|
||||
[a, b, c, d, e, f, g, h, i, j]
|
||||
end
|
||||
ruby
|
||||
|
||||
@a.(1, 2).should == [1, 2, nil, [], nil, [nil], nil, nil, nil, nil]
|
||||
result = @a.(1, [2, [3, 4, 5, [6, [7, 8]], 9], [10, [11, 12]]])
|
||||
result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |*, **k| k }
|
||||
ruby
|
||||
|
||||
@a.().should == {}
|
||||
@a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
|
||||
|
||||
h = mock("keyword splat")
|
||||
h.should_receive(:to_hash).and_return({a: 1})
|
||||
@a.(h).should == {a: 1}
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |*, &b| b }
|
||||
ruby
|
||||
|
||||
@a.().should be_nil
|
||||
@a.(1, 2, 3, 4).should be_nil
|
||||
@a.(&(l = ->{})).should equal(l)
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |a:, b:| [a, b] }
|
||||
ruby
|
||||
|
||||
@a.(a: 1, b: 2).should == [1, 2]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |a:, b: 1| [a, b] }
|
||||
ruby
|
||||
|
||||
@a.(a: 1).should == [1, 1]
|
||||
@a.(a: 1, b: 2).should == [1, 2]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |a: 1, b:| [a, b] }
|
||||
ruby
|
||||
|
||||
@a.(b: 0).should == [1, 0]
|
||||
@a.(b: 2, a: 3).should == [3, 2]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda do |a: (@a = -> (a: 1) { a }), b:|
|
||||
[a, b]
|
||||
end
|
||||
ruby
|
||||
|
||||
@a.(a: 2, b: 3).should == [2, 3]
|
||||
@a.(b: 1).should == [@a, 1]
|
||||
|
||||
# Note the default value of a: in the original method.
|
||||
@a.().should == 1
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda { |a: 1, b: 2| [a, b] }
|
||||
ruby
|
||||
|
||||
@a.().should == [1, 2]
|
||||
@a.(b: 3, a: 4).should == [4, 3]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda do |a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l|
|
||||
[a, b, c, d, e, f, g, h, k, l]
|
||||
end
|
||||
ruby
|
||||
|
||||
result = @a.(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{}))
|
||||
result.should == [9, 8, [7], [], 6, 5, 4, 3, {}, l]
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
@a = lambda do |a, b=1, *c, d, e:, f: 2, g:, **k, &l|
|
||||
[a, b, c, d, e, f, g, k, l]
|
||||
end
|
||||
ruby
|
||||
|
||||
result = @a.(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{}))
|
||||
result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l]
|
||||
end
|
||||
end
|
||||
end
|
45
spec/ruby/language/line_spec.rb
Normal file
45
spec/ruby/language/line_spec.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/code_loading', __FILE__)
|
||||
require File.expand_path('../shared/__LINE__', __FILE__)
|
||||
|
||||
describe "The __LINE__ pseudo-variable" do
|
||||
it "raises a SyntaxError if assigned to" do
|
||||
lambda { eval("__LINE__ = 1") }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
before :each do
|
||||
ScratchPad.record []
|
||||
end
|
||||
|
||||
after :each do
|
||||
ScratchPad.clear
|
||||
end
|
||||
|
||||
it "equals the line number of the text inside an eval" do
|
||||
eval <<-EOC
|
||||
ScratchPad << __LINE__
|
||||
|
||||
# line 3
|
||||
|
||||
ScratchPad << __LINE__
|
||||
EOC
|
||||
|
||||
ScratchPad.recorded.should == [1, 5]
|
||||
end
|
||||
end
|
||||
|
||||
describe "The __LINE__ pseudo-variable" do
|
||||
it_behaves_like :language___LINE__, :require, CodeLoadingSpecs::Method.new
|
||||
end
|
||||
|
||||
describe "The __LINE__ pseudo-variable" do
|
||||
it_behaves_like :language___LINE__, :require, Kernel
|
||||
end
|
||||
|
||||
describe "The __LINE__ pseudo-variable" do
|
||||
it_behaves_like :language___LINE__, :load, CodeLoadingSpecs::Method.new
|
||||
end
|
||||
|
||||
describe "The __LINE__ pseudo-variable" do
|
||||
it_behaves_like :language___LINE__, :load, Kernel
|
||||
end
|
67
spec/ruby/language/loop_spec.rb
Normal file
67
spec/ruby/language/loop_spec.rb
Normal file
|
@ -0,0 +1,67 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "The loop expression" do
|
||||
it "repeats the given block until a break is called" do
|
||||
outer_loop = 0
|
||||
loop do
|
||||
outer_loop += 1
|
||||
break if outer_loop == 10
|
||||
end
|
||||
outer_loop.should == 10
|
||||
end
|
||||
|
||||
it "executes code in its own scope" do
|
||||
loop do
|
||||
inner_loop = 123
|
||||
break
|
||||
end
|
||||
lambda { inner_loop }.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "returns the value passed to break if interrupted by break" do
|
||||
loop do
|
||||
break 123
|
||||
end.should == 123
|
||||
end
|
||||
|
||||
it "returns nil if interrupted by break with no arguments" do
|
||||
loop do
|
||||
break
|
||||
end.should == nil
|
||||
end
|
||||
|
||||
it "skips to end of body with next" do
|
||||
a = []
|
||||
i = 0
|
||||
loop do
|
||||
break if (i+=1) >= 5
|
||||
next if i == 3
|
||||
a << i
|
||||
end
|
||||
a.should == [1, 2, 4]
|
||||
end
|
||||
|
||||
it "restarts the current iteration with redo" do
|
||||
a = []
|
||||
loop do
|
||||
a << 1
|
||||
redo if a.size < 2
|
||||
a << 2
|
||||
break if a.size == 3
|
||||
end
|
||||
a.should == [1, 1, 2]
|
||||
end
|
||||
|
||||
it "uses a spaghetti nightmare of redo, next and break" do
|
||||
a = []
|
||||
loop do
|
||||
a << 1
|
||||
redo if a.size == 1
|
||||
a << 2
|
||||
next if a.size == 3
|
||||
a << 3
|
||||
break if a.size > 6
|
||||
end
|
||||
a.should == [1, 1, 2, 1, 2, 3, 1, 2, 3]
|
||||
end
|
||||
end
|
62
spec/ruby/language/magic_comment_spec.rb
Normal file
62
spec/ruby/language/magic_comment_spec.rb
Normal file
|
@ -0,0 +1,62 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "Magic comment" do
|
||||
it "is optional" do
|
||||
eval("__ENCODING__").should be_an_instance_of(Encoding)
|
||||
end
|
||||
|
||||
it "determines __ENCODING__" do
|
||||
eval(<<EOS.force_encoding("US-ASCII")).should == Encoding::ASCII_8BIT
|
||||
# encoding: ASCII-8BIT
|
||||
__ENCODING__
|
||||
EOS
|
||||
end
|
||||
|
||||
it "is case-insensitive" do
|
||||
eval(<<EOS.force_encoding("US-ASCII")).should == Encoding::ASCII_8BIT
|
||||
# CoDiNg: aScIi-8bIt
|
||||
__ENCODING__
|
||||
EOS
|
||||
end
|
||||
|
||||
it "must be at the first line" do
|
||||
eval(<<EOS.force_encoding("US-ASCII")).should == Encoding::US_ASCII
|
||||
|
||||
# encoding: ASCII-8BIT
|
||||
__ENCODING__
|
||||
EOS
|
||||
end
|
||||
|
||||
it "must be the first token of the line" do
|
||||
eval(<<EOS.force_encoding("US-ASCII")).should == Encoding::US_ASCII
|
||||
1+1 # encoding: ASCII-8BIT
|
||||
__ENCODING__
|
||||
EOS
|
||||
eval(<<EOS.force_encoding("US-ASCII")).should == Encoding::ASCII_8BIT
|
||||
# encoding: ASCII-8BIT
|
||||
__ENCODING__
|
||||
EOS
|
||||
end
|
||||
|
||||
it "can be after the shebang" do
|
||||
eval(<<EOS.force_encoding("US-ASCII")).should == Encoding::ASCII_8BIT
|
||||
#!/usr/bin/ruby -Ku
|
||||
# encoding: ASCII-8BIT
|
||||
__ENCODING__
|
||||
EOS
|
||||
end
|
||||
|
||||
it "can take Emacs style" do
|
||||
eval(<<EOS.force_encoding("US-ASCII")).should == Encoding::ASCII_8BIT
|
||||
# -*- encoding: ascii-8bit -*-
|
||||
__ENCODING__
|
||||
EOS
|
||||
end
|
||||
|
||||
it "can take vim style" do
|
||||
eval(<<EOS.force_encoding("US-ASCII")).should == Encoding::ASCII_8BIT
|
||||
# vim: filetype=ruby, fileencoding=ascii-8bit, tabsize=3, shiftwidth=3
|
||||
__ENCODING__
|
||||
EOS
|
||||
end
|
||||
end
|
74
spec/ruby/language/match_spec.rb
Normal file
74
spec/ruby/language/match_spec.rb
Normal file
|
@ -0,0 +1,74 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/match_operators', __FILE__)
|
||||
|
||||
describe "The !~ operator" do
|
||||
before :each do
|
||||
@obj = OperatorImplementor.new
|
||||
end
|
||||
|
||||
it "evaluates as a call to !~" do
|
||||
expected = "hello world"
|
||||
|
||||
opval = (@obj !~ expected)
|
||||
methodval = @obj.send(:"!~", expected)
|
||||
|
||||
opval.should == expected
|
||||
methodval.should == expected
|
||||
end
|
||||
end
|
||||
|
||||
describe "The =~ operator" do
|
||||
before :each do
|
||||
@impl = OperatorImplementor.new
|
||||
end
|
||||
|
||||
it "calls the =~ method" do
|
||||
expected = "hello world"
|
||||
|
||||
opval = (@obj =~ expected)
|
||||
methodval = @obj.send(:"=~", expected)
|
||||
|
||||
opval.should == expected
|
||||
methodval.should == expected
|
||||
end
|
||||
end
|
||||
|
||||
describe "The =~ operator with named captures" do
|
||||
before :each do
|
||||
@regexp = /(?<matched>foo)(?<unmatched>bar)?/
|
||||
@string = "foofoo"
|
||||
end
|
||||
|
||||
describe "on syntax of /regexp/ =~ string_variable" do
|
||||
it "sets local variables by the captured pairs" do
|
||||
/(?<matched>foo)(?<unmatched>bar)?/ =~ @string
|
||||
local_variables.should == [:matched, :unmatched]
|
||||
matched.should == "foo"
|
||||
unmatched.should == nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "on syntax of string_variable =~ /regexp/" do
|
||||
it "does not set local variables" do
|
||||
@string =~ /(?<matched>foo)(?<unmatched>bar)?/
|
||||
local_variables.should == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "on syntax of regexp_variable =~ string_variable" do
|
||||
it "does not set local variables" do
|
||||
@regexp =~ @string
|
||||
local_variables.should == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "on the method calling" do
|
||||
it "does not set local variables" do
|
||||
@regexp.=~(@string)
|
||||
local_variables.should == []
|
||||
|
||||
@regexp.send :=~, @string
|
||||
local_variables.should == []
|
||||
end
|
||||
end
|
||||
end
|
143
spec/ruby/language/metaclass_spec.rb
Normal file
143
spec/ruby/language/metaclass_spec.rb
Normal file
|
@ -0,0 +1,143 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/class', __FILE__)
|
||||
require File.expand_path('../fixtures/metaclass', __FILE__)
|
||||
|
||||
describe "self in a metaclass body (class << obj)" do
|
||||
it "is TrueClass for true" do
|
||||
class << true; self; end.should == TrueClass
|
||||
end
|
||||
|
||||
it "is FalseClass for false" do
|
||||
class << false; self; end.should == FalseClass
|
||||
end
|
||||
|
||||
it "is NilClass for nil" do
|
||||
class << nil; self; end.should == NilClass
|
||||
end
|
||||
|
||||
it "raises a TypeError for numbers" do
|
||||
lambda { class << 1; self; end }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises a TypeError for symbols" do
|
||||
lambda { class << :symbol; self; end }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "is a singleton Class instance" do
|
||||
cls = class << mock('x'); self; end
|
||||
cls.is_a?(Class).should == true
|
||||
cls.should_not equal(Object)
|
||||
end
|
||||
end
|
||||
|
||||
describe "A constant on a metaclass" do
|
||||
before :each do
|
||||
@object = Object.new
|
||||
class << @object
|
||||
CONST = self
|
||||
end
|
||||
end
|
||||
|
||||
it "can be accessed after the metaclass body is reopened" do
|
||||
class << @object
|
||||
CONST.should == self
|
||||
end
|
||||
end
|
||||
|
||||
it "can be accessed via self::CONST" do
|
||||
class << @object
|
||||
self::CONST.should == self
|
||||
end
|
||||
end
|
||||
|
||||
it "can be accessed via const_get" do
|
||||
class << @object
|
||||
const_get(:CONST).should == self
|
||||
end
|
||||
end
|
||||
|
||||
it "is not defined on the object's class" do
|
||||
@object.class.const_defined?(:CONST).should be_false
|
||||
end
|
||||
|
||||
it "is not defined in the metaclass opener's scope" do
|
||||
class << @object
|
||||
CONST
|
||||
end
|
||||
lambda { CONST }.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "cannot be accessed via object::CONST" do
|
||||
lambda do
|
||||
@object::CONST
|
||||
end.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises a NameError for anonymous_module::CONST" do
|
||||
@object = Class.new
|
||||
class << @object
|
||||
CONST = 100
|
||||
end
|
||||
|
||||
lambda do
|
||||
@object::CONST
|
||||
end.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "appears in the metaclass constant list" do
|
||||
constants = class << @object; constants; end
|
||||
constants.should include(:CONST)
|
||||
end
|
||||
|
||||
it "does not appear in the object's class constant list" do
|
||||
@object.class.constants.should_not include(:CONST)
|
||||
end
|
||||
|
||||
it "is not preserved when the object is duped" do
|
||||
@object = @object.dup
|
||||
|
||||
lambda do
|
||||
class << @object; CONST; end
|
||||
end.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "is preserved when the object is cloned" do
|
||||
@object = @object.clone
|
||||
|
||||
class << @object
|
||||
CONST.should_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "calling methods on the metaclass" do
|
||||
|
||||
it "calls a method on the metaclass" do
|
||||
MetaClassSpecs::A.cheese.should == 'edam'
|
||||
MetaClassSpecs::B.cheese.should == 'stilton'
|
||||
end
|
||||
|
||||
it "calls a method on the instance's metaclass" do
|
||||
b = MetaClassSpecs::B.new
|
||||
b_meta = MetaClassSpecs.metaclass_of b
|
||||
b_meta.send(:define_method, :cheese) {'cheshire'}
|
||||
b.cheese.should == 'cheshire'
|
||||
end
|
||||
|
||||
it "calls a method in deeper chains of metaclasses" do
|
||||
b = MetaClassSpecs::B.new
|
||||
b_meta = MetaClassSpecs.metaclass_of b
|
||||
b_meta_meta = MetaClassSpecs.metaclass_of b_meta
|
||||
b_meta_meta.send(:define_method, :cheese) {'gouda'}
|
||||
b_meta.cheese.should == 'gouda'
|
||||
|
||||
b_meta_meta_meta = MetaClassSpecs.metaclass_of b_meta_meta
|
||||
b_meta_meta_meta.send(:define_method, :cheese) {'wensleydale'}
|
||||
b_meta_meta.cheese.should == 'wensleydale'
|
||||
end
|
||||
|
||||
it "calls a method defined on the metaclass of the metaclass" do
|
||||
d_meta = MetaClassSpecs::D.singleton_class
|
||||
d_meta.ham.should == 'iberico'
|
||||
end
|
||||
end
|
1290
spec/ruby/language/method_spec.rb
Normal file
1290
spec/ruby/language/method_spec.rb
Normal file
File diff suppressed because it is too large
Load diff
91
spec/ruby/language/module_spec.rb
Normal file
91
spec/ruby/language/module_spec.rb
Normal file
|
@ -0,0 +1,91 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/module', __FILE__)
|
||||
|
||||
describe "The module keyword" do
|
||||
it "creates a new module without semicolon" do
|
||||
module ModuleSpecsKeywordWithoutSemicolon end
|
||||
ModuleSpecsKeywordWithoutSemicolon.should be_an_instance_of(Module)
|
||||
end
|
||||
|
||||
it "creates a new module with a non-qualified constant name" do
|
||||
module ModuleSpecsToplevel; end
|
||||
ModuleSpecsToplevel.should be_an_instance_of(Module)
|
||||
end
|
||||
|
||||
it "creates a new module with a qualified constant name" do
|
||||
module ModuleSpecs::Nested; end
|
||||
ModuleSpecs::Nested.should be_an_instance_of(Module)
|
||||
end
|
||||
|
||||
it "creates a new module with a variable qualified constant name" do
|
||||
m = Module.new
|
||||
module m::N; end
|
||||
m::N.should be_an_instance_of(Module)
|
||||
end
|
||||
|
||||
it "reopens an existing module" do
|
||||
module ModuleSpecs; Reopened = true; end
|
||||
ModuleSpecs::Reopened.should be_true
|
||||
end
|
||||
|
||||
it "reopens a module included in Object" do
|
||||
module IncludedModuleSpecs; Reopened = true; end
|
||||
ModuleSpecs::IncludedInObject::IncludedModuleSpecs::Reopened.should be_true
|
||||
end
|
||||
|
||||
it "raises a TypeError if the constant is a Class" do
|
||||
lambda do
|
||||
module ModuleSpecs::Modules::Klass; end
|
||||
end.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises a TypeError if the constant is a String" do
|
||||
lambda { module ModuleSpecs::Modules::A; end }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises a TypeError if the constant is a Fixnum" do
|
||||
lambda { module ModuleSpecs::Modules::B; end }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises a TypeError if the constant is nil" do
|
||||
lambda { module ModuleSpecs::Modules::C; end }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises a TypeError if the constant is true" do
|
||||
lambda { module ModuleSpecs::Modules::D; end }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises a TypeError if the constant is false" do
|
||||
lambda { module ModuleSpecs::Modules::D; end }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Assigning an anonymous module to a constant" do
|
||||
it "sets the name of the module" do
|
||||
mod = Module.new
|
||||
mod.name.should be_nil
|
||||
|
||||
::ModuleSpecs_CS1 = mod
|
||||
mod.name.should == "ModuleSpecs_CS1"
|
||||
end
|
||||
|
||||
it "does not set the name of a module scoped by an anonymous module" do
|
||||
a, b = Module.new, Module.new
|
||||
a::B = b
|
||||
b.name.should be_nil
|
||||
end
|
||||
|
||||
it "sets the name of contained modules when assigning a toplevel anonymous module" do
|
||||
a, b, c, d = Module.new, Module.new, Module.new, Module.new
|
||||
a::B = b
|
||||
a::B::C = c
|
||||
a::B::C::E = c
|
||||
a::D = d
|
||||
|
||||
::ModuleSpecs_CS2 = a
|
||||
a.name.should == "ModuleSpecs_CS2"
|
||||
b.name.should == "ModuleSpecs_CS2::B"
|
||||
c.name.should == "ModuleSpecs_CS2::B::C"
|
||||
d.name.should == "ModuleSpecs_CS2::D"
|
||||
end
|
||||
end
|
410
spec/ruby/language/next_spec.rb
Normal file
410
spec/ruby/language/next_spec.rb
Normal file
|
@ -0,0 +1,410 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/next', __FILE__)
|
||||
|
||||
describe "The next statement from within the block" do
|
||||
before :each do
|
||||
ScratchPad.record []
|
||||
end
|
||||
|
||||
it "ends block execution" do
|
||||
a = []
|
||||
lambda {
|
||||
a << 1
|
||||
next
|
||||
a << 2
|
||||
}.call
|
||||
a.should == [1]
|
||||
end
|
||||
|
||||
it "causes block to return nil if invoked without arguments" do
|
||||
lambda { 123; next; 456 }.call.should == nil
|
||||
end
|
||||
|
||||
it "causes block to return nil if invoked with an empty expression" do
|
||||
lambda { next (); 456 }.call.should be_nil
|
||||
end
|
||||
|
||||
it "returns the argument passed" do
|
||||
lambda { 123; next 234; 345 }.call.should == 234
|
||||
end
|
||||
|
||||
it "returns to the invoking method" do
|
||||
NextSpecs.yielding_method(nil) { next }.should == :method_return_value
|
||||
end
|
||||
|
||||
it "returns to the invoking method, with the specified value" do
|
||||
NextSpecs.yielding_method(nil) {
|
||||
next nil;
|
||||
fail("next didn't end the block execution")
|
||||
}.should == :method_return_value
|
||||
|
||||
NextSpecs.yielding_method(1) {
|
||||
next 1
|
||||
fail("next didn't end the block execution")
|
||||
}.should == :method_return_value
|
||||
|
||||
NextSpecs.yielding_method([1, 2, 3]) {
|
||||
next 1, 2, 3
|
||||
fail("next didn't end the block execution")
|
||||
}.should == :method_return_value
|
||||
end
|
||||
|
||||
it "returns to the currently yielding method in case of chained calls" do
|
||||
class ChainedNextTest
|
||||
def self.meth_with_yield(&b)
|
||||
yield.should == :next_return_value
|
||||
:method_return_value
|
||||
end
|
||||
def self.invoking_method(&b)
|
||||
meth_with_yield(&b)
|
||||
end
|
||||
def self.enclosing_method
|
||||
invoking_method do
|
||||
next :next_return_value
|
||||
:wrong_return_value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ChainedNextTest.enclosing_method.should == :method_return_value
|
||||
end
|
||||
|
||||
it "causes ensure blocks to run" do
|
||||
[1].each do |i|
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
next
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
|
||||
it "skips following code outside an exception block" do
|
||||
3.times do |i|
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
next if i == 0
|
||||
break if i == 2
|
||||
ScratchPad << :begin_end
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
|
||||
ScratchPad << :after
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [
|
||||
:begin, :ensure, :begin, :begin_end, :ensure, :after, :begin, :ensure]
|
||||
end
|
||||
|
||||
it "passes the value returned by a method with omitted parenthesis and passed block" do
|
||||
obj = NextSpecs::Block.new
|
||||
lambda { next obj.method :value do |x| x end }.call.should == :value
|
||||
end
|
||||
end
|
||||
|
||||
describe "The next statement" do
|
||||
describe "in a method" do
|
||||
it "is invalid and raises a SyntaxError" do
|
||||
lambda {
|
||||
eval("def m; next; end")
|
||||
}.should raise_error(SyntaxError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "The next statement" do
|
||||
before :each do
|
||||
ScratchPad.record []
|
||||
end
|
||||
|
||||
describe "in a while loop" do
|
||||
describe "when not passed an argument" do
|
||||
it "causes ensure blocks to run" do
|
||||
NextSpecs.while_next(false)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
|
||||
it "causes ensure blocks to run when nested in an block" do
|
||||
NextSpecs.while_within_iter(false)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
end
|
||||
|
||||
describe "when passed an argument" do
|
||||
it "causes ensure blocks to run" do
|
||||
NextSpecs.while_next(true)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
|
||||
it "causes ensure blocks to run when nested in an block" do
|
||||
NextSpecs.while_within_iter(true)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
end
|
||||
|
||||
it "causes nested ensure blocks to run" do
|
||||
x = true
|
||||
while x
|
||||
begin
|
||||
ScratchPad << :outer_begin
|
||||
x = false
|
||||
begin
|
||||
ScratchPad << :inner_begin
|
||||
next
|
||||
ensure
|
||||
ScratchPad << :inner_ensure
|
||||
end
|
||||
ensure
|
||||
ScratchPad << :outer_ensure
|
||||
end
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:outer_begin, :inner_begin, :inner_ensure, :outer_ensure]
|
||||
end
|
||||
|
||||
it "causes ensure blocks to run when mixed with break" do
|
||||
x = 1
|
||||
while true
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
break if x > 1
|
||||
x += 1
|
||||
next
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure, :begin, :ensure]
|
||||
end
|
||||
end
|
||||
|
||||
describe "in an until loop" do
|
||||
describe "when not passed an argument" do
|
||||
it "causes ensure blocks to run" do
|
||||
NextSpecs.until_next(false)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
|
||||
it "causes ensure blocks to run when nested in an block" do
|
||||
NextSpecs.until_within_iter(false)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
end
|
||||
|
||||
describe "when passed an argument" do
|
||||
it "causes ensure blocks to run" do
|
||||
NextSpecs.until_next(true)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
|
||||
it "causes ensure blocks to run when nested in an block" do
|
||||
NextSpecs.until_within_iter(true)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
end
|
||||
|
||||
it "causes nested ensure blocks to run" do
|
||||
x = false
|
||||
until x
|
||||
begin
|
||||
ScratchPad << :outer_begin
|
||||
x = true
|
||||
begin
|
||||
ScratchPad << :inner_begin
|
||||
next
|
||||
ensure
|
||||
ScratchPad << :inner_ensure
|
||||
end
|
||||
ensure
|
||||
ScratchPad << :outer_ensure
|
||||
end
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:outer_begin, :inner_begin, :inner_ensure, :outer_ensure]
|
||||
end
|
||||
|
||||
it "causes ensure blocks to run when mixed with break" do
|
||||
x = 1
|
||||
until false
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
break if x > 1
|
||||
x += 1
|
||||
next
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure, :begin, :ensure]
|
||||
end
|
||||
end
|
||||
|
||||
describe "in a loop" do
|
||||
describe "when not passed an argument" do
|
||||
it "causes ensure blocks to run" do
|
||||
NextSpecs.loop_next(false)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
|
||||
it "causes ensure blocks to run when nested in an block" do
|
||||
NextSpecs.loop_within_iter(false)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
end
|
||||
|
||||
describe "when passed an argument" do
|
||||
it "causes ensure blocks to run" do
|
||||
NextSpecs.loop_next(true)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
|
||||
it "causes ensure blocks to run when nested in an block" do
|
||||
NextSpecs.loop_within_iter(true)
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure]
|
||||
end
|
||||
end
|
||||
|
||||
it "causes nested ensure blocks to run" do
|
||||
x = 1
|
||||
loop do
|
||||
break if x == 2
|
||||
|
||||
begin
|
||||
ScratchPad << :outer_begin
|
||||
begin
|
||||
ScratchPad << :inner_begin
|
||||
x += 1
|
||||
next
|
||||
ensure
|
||||
ScratchPad << :inner_ensure
|
||||
end
|
||||
ensure
|
||||
ScratchPad << :outer_ensure
|
||||
end
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:outer_begin, :inner_begin, :inner_ensure, :outer_ensure]
|
||||
end
|
||||
|
||||
it "causes ensure blocks to run when mixed with break" do
|
||||
x = 1
|
||||
loop do
|
||||
begin
|
||||
ScratchPad << :begin
|
||||
break if x > 1
|
||||
x += 1
|
||||
next
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
end
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:begin, :ensure, :begin, :ensure]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Assignment via next" do
|
||||
it "assigns objects" do
|
||||
def r(val); a = yield(); val.should == a; end
|
||||
r(nil){next}
|
||||
r(nil){next nil}
|
||||
r(1){next 1}
|
||||
r([]){next []}
|
||||
r([1]){next [1]}
|
||||
r([nil]){next [nil]}
|
||||
r([[]]){next [[]]}
|
||||
r([]){next [*[]]}
|
||||
r([1]){next [*[1]]}
|
||||
r([1,2]){next [*[1,2]]}
|
||||
end
|
||||
|
||||
it "assigns splatted objects" do
|
||||
def r(val); a = yield(); val.should == a; end
|
||||
r([]){next *nil}
|
||||
r([1]){next *1}
|
||||
r([]){next *[]}
|
||||
r([1]){next *[1]}
|
||||
r([nil]){next *[nil]}
|
||||
r([[]]){next *[[]]}
|
||||
r([]){next *[*[]]}
|
||||
r([1]){next *[*[1]]}
|
||||
r([1,2]){next *[*[1,2]]}
|
||||
end
|
||||
|
||||
it "assigns objects to a splatted reference" do
|
||||
def r(val); *a = yield(); val.should == a; end
|
||||
r([nil]){next}
|
||||
r([nil]){next nil}
|
||||
r([1]){next 1}
|
||||
r([]){next []}
|
||||
r([1]){next [1]}
|
||||
r([nil]){next [nil]}
|
||||
r([[]]){next [[]]}
|
||||
r([1,2]){next [1,2]}
|
||||
r([]){next [*[]]}
|
||||
r([1]){next [*[1]]}
|
||||
r([1,2]){next [*[1,2]]}
|
||||
end
|
||||
|
||||
it "assigns splatted objects to a splatted reference via a splatted yield" do
|
||||
def r(val); *a = *yield(); val.should == a; end
|
||||
r([]){next *nil}
|
||||
r([1]){next *1}
|
||||
r([]){next *[]}
|
||||
r([1]){next *[1]}
|
||||
r([nil]){next *[nil]}
|
||||
r([[]]){next *[[]]}
|
||||
r([1,2]){next *[1,2]}
|
||||
r([]){next *[*[]]}
|
||||
r([1]){next *[*[1]]}
|
||||
r([1,2]){next *[*[1,2]]}
|
||||
end
|
||||
|
||||
it "assigns objects to multiple variables" do
|
||||
def r(val); a,b,*c = yield(); val.should == [a,b,c]; end
|
||||
r([nil,nil,[]]){next}
|
||||
r([nil,nil,[]]){next nil}
|
||||
r([1,nil,[]]){next 1}
|
||||
r([nil,nil,[]]){next []}
|
||||
r([1,nil,[]]){next [1]}
|
||||
r([nil,nil,[]]){next [nil]}
|
||||
r([[],nil,[]]){next [[]]}
|
||||
r([1,2,[]]){next [1,2]}
|
||||
r([nil,nil,[]]){next [*[]]}
|
||||
r([1,nil,[]]){next [*[1]]}
|
||||
r([1,2,[]]){next [*[1,2]]}
|
||||
end
|
||||
|
||||
it "assigns splatted objects to multiple variables" do
|
||||
def r(val); a,b,*c = *yield(); val.should == [a,b,c]; end
|
||||
r([nil,nil,[]]){next *nil}
|
||||
r([1,nil,[]]){next *1}
|
||||
r([nil,nil,[]]){next *[]}
|
||||
r([1,nil,[]]){next *[1]}
|
||||
r([nil,nil,[]]){next *[nil]}
|
||||
r([[],nil,[]]){next *[[]]}
|
||||
r([1,2,[]]){next *[1,2]}
|
||||
r([nil,nil,[]]){next *[*[]]}
|
||||
r([1,nil,[]]){next *[*[1]]}
|
||||
r([1,2,[]]){next *[*[1,2]]}
|
||||
end
|
||||
end
|
51
spec/ruby/language/not_spec.rb
Normal file
51
spec/ruby/language/not_spec.rb
Normal file
|
@ -0,0 +1,51 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "The not keyword" do
|
||||
it "negates a `true' value" do
|
||||
(not true).should be_false
|
||||
(not 'true').should be_false
|
||||
end
|
||||
|
||||
it "negates a `false' value" do
|
||||
(not false).should be_true
|
||||
(not nil).should be_true
|
||||
end
|
||||
|
||||
it "accepts an argument" do
|
||||
not(true).should be_false
|
||||
end
|
||||
|
||||
it "returns false if the argument is true" do
|
||||
(not(true)).should be_false
|
||||
end
|
||||
|
||||
it "returns true if the argument is false" do
|
||||
(not(false)).should be_true
|
||||
end
|
||||
|
||||
it "returns true if the argument is nil" do
|
||||
(not(nil)).should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "The `!' keyword" do
|
||||
it "negates a `true' value" do
|
||||
(!true).should be_false
|
||||
(!'true').should be_false
|
||||
end
|
||||
|
||||
it "negates a `false' value" do
|
||||
(!false).should be_true
|
||||
(!nil).should be_true
|
||||
end
|
||||
|
||||
it "doubled turns a truthful object into `true'" do
|
||||
(!!true).should be_true
|
||||
(!!'true').should be_true
|
||||
end
|
||||
|
||||
it "doubled turns a not truthful object into `false'" do
|
||||
(!!false).should be_false
|
||||
(!!nil).should be_false
|
||||
end
|
||||
end
|
97
spec/ruby/language/numbers_spec.rb
Normal file
97
spec/ruby/language/numbers_spec.rb
Normal file
|
@ -0,0 +1,97 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "A number literal" do
|
||||
|
||||
it "can be a sequence of decimal digits" do
|
||||
435.should == 435
|
||||
end
|
||||
|
||||
it "can have '_' characters between digits" do
|
||||
4_3_5_7.should == 4357
|
||||
end
|
||||
|
||||
it "cannot have a leading underscore" do
|
||||
lambda { eval("_4_2") }.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it "can have a decimal point" do
|
||||
4.35.should == 4.35
|
||||
end
|
||||
|
||||
it "must have a digit before the decimal point" do
|
||||
0.75.should == 0.75
|
||||
lambda { eval(".75") }.should raise_error(SyntaxError)
|
||||
lambda { eval("-.75") }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "can have an exponent" do
|
||||
1.2e-3.should == 0.0012
|
||||
end
|
||||
|
||||
it "can be a sequence of hexadecimal digits with a leading '0x'" do
|
||||
0xffff.should == 65535
|
||||
end
|
||||
|
||||
it "can be a sequence of binary digits with a leading '0x'" do
|
||||
0b01011.should == 11
|
||||
end
|
||||
|
||||
it "can be a sequence of octal digits with a leading '0'" do
|
||||
0377.should == 255
|
||||
end
|
||||
|
||||
it "can be an integer literal with trailing 'r' to represent a Rational" do
|
||||
eval('3r').should == Rational(3, 1)
|
||||
eval('-3r').should == Rational(-3, 1)
|
||||
end
|
||||
|
||||
it "can be an bignum literal with trailing 'r' to represent a Rational" do
|
||||
eval('1111111111111111111111111111111111111111111111r').should == Rational(1111111111111111111111111111111111111111111111, 1)
|
||||
eval('-1111111111111111111111111111111111111111111111r').should == Rational(-1111111111111111111111111111111111111111111111, 1)
|
||||
end
|
||||
|
||||
it "can be a decimal literal with trailing 'r' to represent a Rational" do
|
||||
eval('0.3r').should == Rational(3, 10)
|
||||
eval('-0.3r').should == Rational(-3, 10)
|
||||
end
|
||||
|
||||
it "can be a hexadecimal literal with trailing 'r' to represent a Rational" do
|
||||
eval('0xffr').should == Rational(255, 1)
|
||||
eval('-0xffr').should == Rational(-255, 1)
|
||||
end
|
||||
|
||||
it "can be an octal literal with trailing 'r' to represent a Rational" do
|
||||
eval('042r').should == Rational(34, 1)
|
||||
eval('-042r').should == Rational(-34, 1)
|
||||
end
|
||||
|
||||
it "can be a binary literal with trailing 'r' to represent a Rational" do
|
||||
eval('0b1111r').should == Rational(15, 1)
|
||||
eval('-0b1111r').should == Rational(-15, 1)
|
||||
end
|
||||
|
||||
it "can be an integer literal with trailing 'i' to represent a Complex" do
|
||||
eval('5i').should == Complex(0, 5)
|
||||
eval('-5i').should == Complex(0, -5)
|
||||
end
|
||||
|
||||
it "can be a decimal literal with trailing 'i' to represent a Complex" do
|
||||
eval('0.6i').should == Complex(0, 0.6)
|
||||
eval('-0.6i').should == Complex(0, -0.6)
|
||||
end
|
||||
|
||||
it "can be a hexadecimal literal with trailing 'i' to represent a Complex" do
|
||||
eval('0xffi').should == Complex(0, 255)
|
||||
eval('-0xffi').should == Complex(0, -255)
|
||||
end
|
||||
|
||||
it "can be a octal literal with trailing 'i' to represent a Complex" do
|
||||
eval("042i").should == Complex(0, 34)
|
||||
eval("-042i").should == Complex(0, -34)
|
||||
end
|
||||
|
||||
it "can be a binary literal with trailing 'i' to represent a Complex" do
|
||||
eval('0b1110i').should == Complex(0, 14)
|
||||
eval('-0b1110i').should == Complex(0, -14)
|
||||
end
|
||||
end
|
226
spec/ruby/language/optional_assignments_spec.rb
Normal file
226
spec/ruby/language/optional_assignments_spec.rb
Normal file
|
@ -0,0 +1,226 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe 'Optional variable assignments' do
|
||||
describe 'using ||=' do
|
||||
describe 'using a single variable' do
|
||||
it 'assigns a new variable' do
|
||||
a ||= 10
|
||||
|
||||
a.should == 10
|
||||
end
|
||||
|
||||
it 're-assigns an existing variable set to false' do
|
||||
a = false
|
||||
a ||= 10
|
||||
|
||||
a.should == 10
|
||||
end
|
||||
|
||||
it 're-assigns an existing variable set to nil' do
|
||||
a = nil
|
||||
a ||= 10
|
||||
|
||||
a.should == 10
|
||||
end
|
||||
|
||||
it 'does not re-assign a variable with a truthy value' do
|
||||
a = 10
|
||||
a ||= 20
|
||||
|
||||
a.should == 10
|
||||
end
|
||||
|
||||
it 'does not evaluate the right side when not needed' do
|
||||
a = 10
|
||||
a ||= raise('should not be executed')
|
||||
a.should == 10
|
||||
end
|
||||
|
||||
it 'does not re-assign a variable with a truthy value when using an inline rescue' do
|
||||
a = 10
|
||||
a ||= 20 rescue 30
|
||||
|
||||
a.should == 10
|
||||
end
|
||||
end
|
||||
|
||||
describe 'using a accessor' do
|
||||
before do
|
||||
klass = Class.new { attr_accessor :b }
|
||||
@a = klass.new
|
||||
end
|
||||
|
||||
it 'assigns a new variable' do
|
||||
@a.b ||= 10
|
||||
|
||||
@a.b.should == 10
|
||||
end
|
||||
|
||||
it 're-assigns an existing variable set to false' do
|
||||
@a.b = false
|
||||
@a.b ||= 10
|
||||
|
||||
@a.b.should == 10
|
||||
end
|
||||
|
||||
it 're-assigns an existing variable set to nil' do
|
||||
@a.b = nil
|
||||
@a.b ||= 10
|
||||
|
||||
@a.b.should == 10
|
||||
end
|
||||
|
||||
it 'does not re-assign a variable with a truthy value' do
|
||||
@a.b = 10
|
||||
@a.b ||= 20
|
||||
|
||||
@a.b.should == 10
|
||||
end
|
||||
|
||||
it 'does not evaluate the right side when not needed' do
|
||||
@a.b = 10
|
||||
@a.b ||= raise('should not be executed')
|
||||
@a.b.should == 10
|
||||
end
|
||||
|
||||
it 'does not re-assign a variable with a truthy value when using an inline rescue' do
|
||||
@a.b = 10
|
||||
@a.b ||= 20 rescue 30
|
||||
|
||||
@a.b.should == 10
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'using &&=' do
|
||||
describe 'using a single variable' do
|
||||
it 'leaves new variable unassigned' do
|
||||
a &&= 10
|
||||
|
||||
a.should == nil
|
||||
end
|
||||
|
||||
it 'leaves false' do
|
||||
a = false
|
||||
a &&= 10
|
||||
|
||||
a.should == false
|
||||
end
|
||||
|
||||
it 'leaves nil' do
|
||||
a = nil
|
||||
a &&= 10
|
||||
|
||||
a.should == nil
|
||||
end
|
||||
|
||||
it 'does not evaluate the right side when not needed' do
|
||||
a = nil
|
||||
a &&= raise('should not be executed')
|
||||
a.should == nil
|
||||
end
|
||||
|
||||
it 'does re-assign a variable with a truthy value' do
|
||||
a = 10
|
||||
a &&= 20
|
||||
|
||||
a.should == 20
|
||||
end
|
||||
|
||||
it 'does re-assign a variable with a truthy value when using an inline rescue' do
|
||||
a = 10
|
||||
a &&= 20 rescue 30
|
||||
|
||||
a.should == 20
|
||||
end
|
||||
end
|
||||
|
||||
describe 'using a single variable' do
|
||||
before do
|
||||
klass = Class.new { attr_accessor :b }
|
||||
@a = klass.new
|
||||
end
|
||||
|
||||
it 'leaves new variable unassigned' do
|
||||
@a.b &&= 10
|
||||
|
||||
@a.b.should == nil
|
||||
end
|
||||
|
||||
it 'leaves false' do
|
||||
@a.b = false
|
||||
@a.b &&= 10
|
||||
|
||||
@a.b.should == false
|
||||
end
|
||||
|
||||
it 'leaves nil' do
|
||||
@a.b = nil
|
||||
@a.b &&= 10
|
||||
|
||||
@a.b.should == nil
|
||||
end
|
||||
|
||||
it 'does not evaluate the right side when not needed' do
|
||||
@a.b = nil
|
||||
@a.b &&= raise('should not be executed')
|
||||
@a.b.should == nil
|
||||
end
|
||||
|
||||
it 'does re-assign a variable with a truthy value' do
|
||||
@a.b = 10
|
||||
@a.b &&= 20
|
||||
|
||||
@a.b.should == 20
|
||||
end
|
||||
|
||||
it 'does re-assign a variable with a truthy value when using an inline rescue' do
|
||||
@a.b = 10
|
||||
@a.b &&= 20 rescue 30
|
||||
|
||||
@a.b.should == 20
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'using compunded constants' do
|
||||
before do
|
||||
Object.send(:remove_const, :A) if defined? Object::A
|
||||
end
|
||||
|
||||
it 'with ||= assignments' do
|
||||
Object::A ||= 10
|
||||
Object::A.should == 10
|
||||
end
|
||||
|
||||
it 'with ||= do not reassign' do
|
||||
Object::A = 20
|
||||
Object::A ||= 10
|
||||
Object::A.should == 20
|
||||
end
|
||||
|
||||
it 'with &&= assignments' do
|
||||
Object::A = 20
|
||||
-> {
|
||||
Object::A &&= 10
|
||||
}.should complain(/already initialized constant/)
|
||||
Object::A.should == 10
|
||||
end
|
||||
|
||||
it 'with &&= assignments will fail with non-existant constants' do
|
||||
lambda { Object::A &&= 10 }.should raise_error(NameError)
|
||||
end
|
||||
|
||||
it 'with operator assignments' do
|
||||
Object::A = 20
|
||||
-> {
|
||||
Object::A += 10
|
||||
}.should complain(/already initialized constant/)
|
||||
Object::A.should == 30
|
||||
end
|
||||
|
||||
it 'with operator assignments will fail with non-existant constants' do
|
||||
lambda { Object::A += 10 }.should raise_error(NameError)
|
||||
end
|
||||
end
|
||||
end
|
90
spec/ruby/language/or_spec.rb
Normal file
90
spec/ruby/language/or_spec.rb
Normal file
|
@ -0,0 +1,90 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "The || operator" do
|
||||
it "evaluates to true if any of its operands are true" do
|
||||
if false || true || nil
|
||||
x = true
|
||||
end
|
||||
x.should == true
|
||||
end
|
||||
|
||||
it "evaluated to false if all of its operands are false" do
|
||||
if false || nil
|
||||
x = true
|
||||
end
|
||||
x.should == nil
|
||||
end
|
||||
|
||||
it "is evaluated before assignment operators" do
|
||||
x = nil || true
|
||||
x.should == true
|
||||
end
|
||||
|
||||
it "has a lower precedence than the && operator" do
|
||||
x = 1 || false && x = 2
|
||||
x.should == 1
|
||||
end
|
||||
|
||||
it "treats empty expressions as nil" do
|
||||
(() || true).should be_true
|
||||
(() || false).should be_false
|
||||
(true || ()).should be_true
|
||||
(false || ()).should be_nil
|
||||
(() || ()).should be_nil
|
||||
end
|
||||
|
||||
it "has a higher precedence than 'break' in 'break true || false'" do
|
||||
# see also 'break true or false' below
|
||||
lambda { break false || true }.call.should be_true
|
||||
end
|
||||
|
||||
it "has a higher precedence than 'next' in 'next true || false'" do
|
||||
lambda { next false || true }.call.should be_true
|
||||
end
|
||||
|
||||
it "has a higher precedence than 'return' in 'return true || false'" do
|
||||
lambda { return false || true }.call.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "The or operator" do
|
||||
it "evaluates to true if any of its operands are true" do
|
||||
x = nil
|
||||
if false or true
|
||||
x = true
|
||||
end
|
||||
x.should == true
|
||||
end
|
||||
|
||||
it "is evaluated after variables are assigned" do
|
||||
x = nil or true
|
||||
x.should == nil
|
||||
end
|
||||
|
||||
it "has a lower precedence than the || operator" do
|
||||
x,y = nil
|
||||
x = true || false or y = 1
|
||||
y.should == nil
|
||||
end
|
||||
|
||||
it "treats empty expressions as nil" do
|
||||
(() or true).should be_true
|
||||
(() or false).should be_false
|
||||
(true or ()).should be_true
|
||||
(false or ()).should be_nil
|
||||
(() or ()).should be_nil
|
||||
end
|
||||
|
||||
it "has a lower precedence than 'break' in 'break true or false'" do
|
||||
# see also 'break true || false' above
|
||||
lambda { eval "break true or false" }.should raise_error(SyntaxError, /void value expression/)
|
||||
end
|
||||
|
||||
it "has a lower precedence than 'next' in 'next true or false'" do
|
||||
lambda { eval "next true or false" }.should raise_error(SyntaxError, /void value expression/)
|
||||
end
|
||||
|
||||
it "has a lower precedence than 'return' in 'return true or false'" do
|
||||
lambda { eval "return true or false" }.should raise_error(SyntaxError, /void value expression/)
|
||||
end
|
||||
end
|
75
spec/ruby/language/order_spec.rb
Normal file
75
spec/ruby/language/order_spec.rb
Normal file
|
@ -0,0 +1,75 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "A method call" do
|
||||
before :each do
|
||||
@obj = Object.new
|
||||
def @obj.foo0(&a)
|
||||
[a ? a.call : nil]
|
||||
end
|
||||
def @obj.foo1(a, &b)
|
||||
[a, b ? b.call : nil]
|
||||
end
|
||||
def @obj.foo2(a, b, &c)
|
||||
[a, b, c ? c.call : nil]
|
||||
end
|
||||
def @obj.foo3(a, b, c, &d)
|
||||
[a, b, c, d ? d.call : nil]
|
||||
end
|
||||
def @obj.foo4(a, b, c, d, &e)
|
||||
[a, b, c, d, e ? e.call : nil]
|
||||
end
|
||||
end
|
||||
|
||||
it "evaluates the receiver first" do
|
||||
(obj = @obj).foo1(obj = nil).should == [nil, nil]
|
||||
(obj = @obj).foo2(obj = nil, obj = nil).should == [nil, nil, nil]
|
||||
(obj = @obj).foo3(obj = nil, obj = nil, obj = nil).should == [nil, nil, nil, nil]
|
||||
(obj = @obj).foo4(obj = nil, obj = nil, obj = nil, obj = nil).should == [nil, nil, nil, nil, nil]
|
||||
end
|
||||
|
||||
it "evaluates arguments after receiver" do
|
||||
a = 0
|
||||
(a += 1; @obj).foo1(a).should == [1, nil]
|
||||
(a += 1; @obj).foo2(a, a).should == [2, 2, nil]
|
||||
(a += 1; @obj).foo3(a, a, a).should == [3, 3, 3, nil]
|
||||
(a += 1; @obj).foo4(a, a, a, a).should == [4, 4, 4, 4, nil]
|
||||
a.should == 4
|
||||
end
|
||||
|
||||
it "evaluates arguments left-to-right" do
|
||||
a = 0
|
||||
@obj.foo1(a += 1).should == [1, nil]
|
||||
@obj.foo2(a += 1, a += 1).should == [2, 3, nil]
|
||||
@obj.foo3(a += 1, a += 1, a += 1).should == [4, 5, 6, nil]
|
||||
@obj.foo4(a += 1, a += 1, a += 1, a += 1).should == [7, 8, 9, 10, nil]
|
||||
a.should == 10
|
||||
end
|
||||
|
||||
it "evaluates block pass after arguments" do
|
||||
a = 0
|
||||
p = proc {true}
|
||||
@obj.foo1(a += 1, &(a += 1; p)).should == [1, true]
|
||||
@obj.foo2(a += 1, a += 1, &(a += 1; p)).should == [3, 4, true]
|
||||
@obj.foo3(a += 1, a += 1, a += 1, &(a += 1; p)).should == [6, 7, 8, true]
|
||||
@obj.foo4(a += 1, a += 1, a += 1, a += 1, &(a += 1; p)).should == [10, 11, 12, 13, true]
|
||||
a.should == 14
|
||||
end
|
||||
|
||||
it "evaluates block pass after receiver" do
|
||||
p1 = proc {true}
|
||||
p2 = proc {false}
|
||||
p1.should_not == p2
|
||||
|
||||
p = p1
|
||||
(p = p2; @obj).foo0(&p).should == [false]
|
||||
p = p1
|
||||
(p = p2; @obj).foo1(1, &p).should == [1, false]
|
||||
p = p1
|
||||
(p = p2; @obj).foo2(1, 1, &p).should == [1, 1, false]
|
||||
p = p1
|
||||
(p = p2; @obj).foo3(1, 1, 1, &p).should == [1, 1, 1, false]
|
||||
p = p1
|
||||
(p = p2; @obj).foo4(1, 1, 1, 1, &p).should == [1, 1, 1, 1, false]
|
||||
p = p1
|
||||
end
|
||||
end
|
448
spec/ruby/language/precedence_spec.rb
Normal file
448
spec/ruby/language/precedence_spec.rb
Normal file
|
@ -0,0 +1,448 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/precedence', __FILE__)
|
||||
|
||||
# Specifying the behavior of operators in combination could
|
||||
# lead to combinatorial explosion. A better way seems to be
|
||||
# to use a technique from formal proofs that involve a set of
|
||||
# equivalent statements. Suppose you have statements A, B, C.
|
||||
# If they are claimed to be equivalent, this can be shown by
|
||||
# proving that A implies B, B implies C, and C implies A.
|
||||
# (Actually any closed circuit of implications.)
|
||||
#
|
||||
# Here, we can use a similar technique where we show starting
|
||||
# at the top that each level of operator has precedence over
|
||||
# the level below (as well as showing associativity within
|
||||
# the precedence level).
|
||||
|
||||
=begin
|
||||
Excerpted from 'Programming Ruby: The Pragmatic Programmer's Guide'
|
||||
Second Edition by Dave Thomas, Chad Fowler, and Andy Hunt, page 324
|
||||
|
||||
Table 22.4. Ruby operators (high to low precedence)
|
||||
Method Operator Description
|
||||
-----------------------------------------------------------------------
|
||||
:: .
|
||||
x* [ ] [ ]= Element reference, element set
|
||||
x ** Exponentiation
|
||||
x ! ~ + - Not, complement, unary plus and minus
|
||||
(method names for the last two are +@ and -@)
|
||||
x * / % Multiply, divide, and modulo
|
||||
x + - Plus and minus
|
||||
x >> << Right and left shift
|
||||
x & “And” (bitwise for integers)
|
||||
x ^ | Exclusive “or” and regular “or” (bitwise for integers)
|
||||
x <= < > >= Comparison operators
|
||||
x <=> == === != =~ !~ Equality and pattern match operators (!=
|
||||
and !~ may not be defined as methods)
|
||||
&& Logical “and”
|
||||
|| Logical “or”
|
||||
.. ... Range (inclusive and exclusive)
|
||||
? : Ternary if-then-else
|
||||
= %= /= -= += |= &= Assignment
|
||||
>>= <<= *= &&= ||= **=
|
||||
defined? Check if symbol defined
|
||||
not Logical negation
|
||||
or and Logical composition
|
||||
if unless while until Expression modifiers
|
||||
begin/end Block expression
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
* Operators marked with 'x' in the Method column are implemented as methods
|
||||
and can be overridden (except != and !~ as noted). (But see the specs
|
||||
below for implementations that define != and !~ as methods.)
|
||||
|
||||
** These are not included in the excerpted table but are shown here for
|
||||
completeness.
|
||||
=end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# It seems that this table is not correct anymore
|
||||
# The correct table derived from MRI's parse.y is as follows:
|
||||
#
|
||||
# Operator Assoc Description
|
||||
#---------------------------------------------------------------
|
||||
# ! ~ + > Not, complement, unary plus
|
||||
# ** > Exponentiation
|
||||
# - > Unary minus
|
||||
# * / % < Multiply, divide, and modulo
|
||||
# + - < Plus and minus
|
||||
# >> << < Right and left shift
|
||||
# & < “And” (bitwise for integers)
|
||||
# ^ | < Exclusive “or” and regular “or” (bitwise for integers)
|
||||
# <= < > >= < Comparison operators
|
||||
# <=> == === != =~ !~ no Equality and pattern match operators (!=
|
||||
# and !~ may not be defined as methods)
|
||||
# && < Logical “and”
|
||||
# || < Logical “or”
|
||||
# .. ... no Range (inclusive and exclusive)
|
||||
# ? : > Ternary if-then-else
|
||||
# rescue < Rescue modifier
|
||||
# = %= /= -= += |= &= > Assignment
|
||||
# >>= <<= *= &&= ||= **=
|
||||
# defined? no Check if symbol defined
|
||||
# not > Logical negation
|
||||
# or and < Logical composition
|
||||
# if unless while until no Expression modifiers
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# [] and []= seem to fall out of here, as well as begin/end
|
||||
#
|
||||
|
||||
# TODO: Resolve these two tables with actual specs. As the comment at the
|
||||
# top suggests, these specs need to be reorganized into a single describe
|
||||
# block for each operator. The describe block should include an example
|
||||
# for associativity (if relevant), an example for any short circuit behavior
|
||||
# (e.g. &&, ||, etc.) and an example block for each operator over which the
|
||||
# instant operator has immediately higher precedence.
|
||||
|
||||
describe "Operators" do
|
||||
it "! ~ + is right-associative" do
|
||||
(!!true).should == true
|
||||
(~~0).should == 0
|
||||
(++2).should == 2
|
||||
end
|
||||
|
||||
it "** is right-associative" do
|
||||
(2**2**3).should == 256
|
||||
end
|
||||
|
||||
it "** has higher precedence than unary minus" do
|
||||
(-2**2).should == -4
|
||||
end
|
||||
|
||||
it "unary minus is right-associative" do
|
||||
(--2).should == 2
|
||||
end
|
||||
|
||||
it "unary minus has higher precedence than * / %" do
|
||||
class UnaryMinusTest; def -@; 50; end; end
|
||||
b = UnaryMinusTest.new
|
||||
|
||||
(-b * 5).should == 250
|
||||
(-b / 5).should == 10
|
||||
(-b % 7).should == 1
|
||||
end
|
||||
|
||||
it "treats +/- as a regular send if the arguments are known locals or block locals" do
|
||||
a = PrecedenceSpecs::NonUnaryOpTest.new
|
||||
a.add_num(1).should == [3]
|
||||
a.sub_num(1).should == [1]
|
||||
a.add_str.should == ['11']
|
||||
a.add_var.should == [2]
|
||||
end
|
||||
|
||||
it "* / % are left-associative" do
|
||||
(2*1/2).should == (2*1)/2
|
||||
# Guard against the Mathn library
|
||||
# TODO: Make these specs not rely on specific behaviour / result values
|
||||
# by using mocks.
|
||||
conflicts_with :Prime do
|
||||
(2*1/2).should_not == 2*(1/2)
|
||||
end
|
||||
|
||||
(10/7/5).should == (10/7)/5
|
||||
(10/7/5).should_not == 10/(7/5)
|
||||
|
||||
(101 % 55 % 7).should == (101 % 55) % 7
|
||||
(101 % 55 % 7).should_not == 101 % (55 % 7)
|
||||
|
||||
(50*20/7%42).should == ((50*20)/7)%42
|
||||
(50*20/7%42).should_not == 50*(20/(7%42))
|
||||
end
|
||||
|
||||
it "* / % have higher precedence than + -" do
|
||||
(2+2*2).should == 6
|
||||
(1+10/5).should == 3
|
||||
(2+10%5).should == 2
|
||||
|
||||
(2-2*2).should == -2
|
||||
(1-10/5).should == -1
|
||||
(10-10%4).should == 8
|
||||
end
|
||||
|
||||
it "+ - are left-associative" do
|
||||
(2-3-4).should == -5
|
||||
(4-3+2).should == 3
|
||||
|
||||
binary_plus = Class.new(String) do
|
||||
alias_method :plus, :+
|
||||
def +(a)
|
||||
plus(a) + "!"
|
||||
end
|
||||
end
|
||||
s = binary_plus.new("a")
|
||||
|
||||
(s+s+s).should == (s+s)+s
|
||||
(s+s+s).should_not == s+(s+s)
|
||||
end
|
||||
|
||||
it "+ - have higher precedence than >> <<" do
|
||||
(2<<1+2).should == 16
|
||||
(8>>1+2).should == 1
|
||||
(4<<1-3).should == 1
|
||||
(2>>1-3).should == 8
|
||||
end
|
||||
|
||||
it ">> << are left-associative" do
|
||||
(1 << 2 << 3).should == 32
|
||||
(10 >> 1 >> 1).should == 2
|
||||
(10 << 4 >> 1).should == 80
|
||||
end
|
||||
|
||||
it ">> << have higher precedence than &" do
|
||||
(4 & 2 << 1).should == 4
|
||||
(2 & 4 >> 1).should == 2
|
||||
end
|
||||
|
||||
it "& is left-associative" do
|
||||
class BitwiseAndTest; def &(a); a+1; end; end
|
||||
c = BitwiseAndTest.new
|
||||
|
||||
(c & 5 & 2).should == (c & 5) & 2
|
||||
(c & 5 & 2).should_not == c & (5 & 2)
|
||||
end
|
||||
|
||||
it "& has higher precedence than ^ |" do
|
||||
(8 ^ 16 & 16).should == 24
|
||||
(8 | 16 & 16).should == 24
|
||||
end
|
||||
|
||||
it "^ | are left-associative" do
|
||||
class OrAndXorTest; def ^(a); a+10; end; def |(a); a-10; end; end
|
||||
d = OrAndXorTest.new
|
||||
|
||||
(d ^ 13 ^ 16).should == (d ^ 13) ^ 16
|
||||
(d ^ 13 ^ 16).should_not == d ^ (13 ^ 16)
|
||||
|
||||
(d | 13 | 4).should == (d | 13) | 4
|
||||
(d | 13 | 4).should_not == d | (13 | 4)
|
||||
end
|
||||
|
||||
it "^ | have higher precedence than <= < > >=" do
|
||||
(10 <= 7 ^ 7).should == false
|
||||
(10 < 7 ^ 7).should == false
|
||||
(10 > 7 ^ 7).should == true
|
||||
(10 >= 7 ^ 7).should == true
|
||||
(10 <= 7 | 7).should == false
|
||||
(10 < 7 | 7).should == false
|
||||
(10 > 7 | 7).should == true
|
||||
(10 >= 7 | 7).should == true
|
||||
end
|
||||
|
||||
it "<= < > >= are left-associative" do
|
||||
class ComparisonTest
|
||||
def <=(a); 0; end;
|
||||
def <(a); 0; end;
|
||||
def >(a); 0; end;
|
||||
def >=(a); 0; end;
|
||||
end
|
||||
|
||||
e = ComparisonTest.new
|
||||
|
||||
(e <= 0 <= 1).should == (e <= 0) <= 1
|
||||
(e <= 0 <= 1).should_not == e <= (0 <= 1)
|
||||
|
||||
(e < 0 < 1).should == (e < 0) < 1
|
||||
(e < 0 < 1).should_not == e < (0 < 1)
|
||||
|
||||
(e >= 0 >= 1).should == (e >= 0) >= 1
|
||||
(e >= 0 >= 1).should_not == e >= (0 >= 1)
|
||||
|
||||
(e > 0 > 1).should == (e > 0) > 1
|
||||
(e > 0 > 1).should_not == e > (0 > 1)
|
||||
end
|
||||
|
||||
it "<=> == === != =~ !~ are non-associative" do
|
||||
lambda { eval("1 <=> 2 <=> 3") }.should raise_error(SyntaxError)
|
||||
lambda { eval("1 == 2 == 3") }.should raise_error(SyntaxError)
|
||||
lambda { eval("1 === 2 === 3") }.should raise_error(SyntaxError)
|
||||
lambda { eval("1 != 2 != 3") }.should raise_error(SyntaxError)
|
||||
lambda { eval("1 =~ 2 =~ 3") }.should raise_error(SyntaxError)
|
||||
lambda { eval("1 !~ 2 !~ 3") }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "<=> == === != =~ !~ have higher precedence than &&" do
|
||||
(false && 2 <=> 3).should == false
|
||||
(false && 3 == false).should == false
|
||||
(false && 3 === false).should == false
|
||||
(false && 3 != true).should == false
|
||||
|
||||
class FalseClass; def =~(o); o == false; end; end
|
||||
(false && true =~ false).should == (false && (true =~ false))
|
||||
(false && true =~ false).should_not == ((false && true) =~ false)
|
||||
class FalseClass; undef_method :=~; end
|
||||
|
||||
(false && true !~ true).should == false
|
||||
end
|
||||
|
||||
# XXX: figure out how to test it
|
||||
# (a && b) && c equals to a && (b && c) for all a,b,c values I can imagine so far
|
||||
it "&& is left-associative"
|
||||
|
||||
it "&& has higher precedence than ||" do
|
||||
(true || false && false).should == true
|
||||
end
|
||||
|
||||
# XXX: figure out how to test it
|
||||
it "|| is left-associative"
|
||||
|
||||
it "|| has higher precedence than .. ..." do
|
||||
(1..false||10).should == (1..10)
|
||||
(1...false||10).should == (1...10)
|
||||
end
|
||||
|
||||
it ".. ... are non-associative" do
|
||||
lambda { eval("1..2..3") }.should raise_error(SyntaxError)
|
||||
lambda { eval("1...2...3") }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
# XXX: this is commented now due to a bug in compiler, which cannot
|
||||
# distinguish between range and flip-flop operator so far. zenspider is
|
||||
# currently working on a new lexer, which will be able to do that.
|
||||
# As soon as it's done, these piece should be reenabled.
|
||||
#
|
||||
# it ".. ... have higher precedence than ? :" do
|
||||
# (1..2 ? 3 : 4).should == 3
|
||||
# (1...2 ? 3 : 4).should == 3
|
||||
# end
|
||||
|
||||
it "? : is right-associative" do
|
||||
(true ? 2 : 3 ? 4 : 5).should == 2
|
||||
end
|
||||
|
||||
def oops; raise end
|
||||
|
||||
it "? : has higher precedence than rescue" do
|
||||
(true ? oops : 0 rescue 10).should == 10
|
||||
end
|
||||
|
||||
# XXX: figure how to test it (problem similar to || associativity)
|
||||
it "rescue is left-associative"
|
||||
|
||||
it "rescue has higher precedence than =" do
|
||||
a = oops rescue 10
|
||||
a.should == 10
|
||||
|
||||
# rescue doesn't have the same sense for %= /= and friends
|
||||
end
|
||||
|
||||
it "= %= /= -= += |= &= >>= <<= *= &&= ||= **= are right-associative" do
|
||||
a = b = 10
|
||||
a.should == 10
|
||||
b.should == 10
|
||||
|
||||
a = b = 10
|
||||
a %= b %= 3
|
||||
a.should == 0
|
||||
b.should == 1
|
||||
|
||||
a = b = 10
|
||||
a /= b /= 2
|
||||
a.should == 2
|
||||
b.should == 5
|
||||
|
||||
a = b = 10
|
||||
a -= b -= 2
|
||||
a.should == 2
|
||||
b.should == 8
|
||||
|
||||
a = b = 10
|
||||
a += b += 2
|
||||
a.should == 22
|
||||
b.should == 12
|
||||
|
||||
a,b = 32,64
|
||||
a |= b |= 2
|
||||
a.should == 98
|
||||
b.should == 66
|
||||
|
||||
a,b = 25,13
|
||||
a &= b &= 7
|
||||
a.should == 1
|
||||
b.should == 5
|
||||
|
||||
a,b=8,2
|
||||
a >>= b >>= 1
|
||||
a.should == 4
|
||||
b.should == 1
|
||||
|
||||
a,b=8,2
|
||||
a <<= b <<= 1
|
||||
a.should == 128
|
||||
b.should == 4
|
||||
|
||||
a,b=8,2
|
||||
a *= b *= 2
|
||||
a.should == 32
|
||||
b.should == 4
|
||||
|
||||
a,b=10,20
|
||||
a &&= b &&= false
|
||||
a.should == false
|
||||
b.should == false
|
||||
|
||||
a,b=nil,nil
|
||||
a ||= b ||= 10
|
||||
a.should == 10
|
||||
b.should == 10
|
||||
|
||||
a,b=2,3
|
||||
a **= b **= 2
|
||||
a.should == 512
|
||||
b.should == 9
|
||||
end
|
||||
|
||||
it "= %= /= -= += |= &= >>= <<= *= &&= ||= **= have higher precedence than defined? operator" do
|
||||
(defined? a = 10).should == "assignment"
|
||||
(defined? a %= 10).should == "assignment"
|
||||
(defined? a /= 10).should == "assignment"
|
||||
(defined? a -= 10).should == "assignment"
|
||||
(defined? a += 10).should == "assignment"
|
||||
(defined? a |= 10).should == "assignment"
|
||||
(defined? a &= 10).should == "assignment"
|
||||
(defined? a >>= 10).should == "assignment"
|
||||
(defined? a <<= 10).should == "assignment"
|
||||
(defined? a *= 10).should == "assignment"
|
||||
(defined? a &&= 10).should == "assignment"
|
||||
(defined? a ||= 10).should == "assignment"
|
||||
(defined? a **= 10).should == "assignment"
|
||||
end
|
||||
|
||||
# XXX: figure out how to test it
|
||||
it "defined? is non-associative"
|
||||
|
||||
it "defined? has higher precedence than not" do
|
||||
# does it have sense?
|
||||
(not defined? qqq).should == true
|
||||
end
|
||||
|
||||
it "not is right-associative" do
|
||||
(not not false).should == false
|
||||
(not not 10).should == true
|
||||
end
|
||||
|
||||
it "not has higher precedence than or/and" do
|
||||
(not false and false).should == false
|
||||
(not false or true).should == true
|
||||
end
|
||||
|
||||
# XXX: figure out how to test it
|
||||
it "or/and are left-associative"
|
||||
|
||||
it "or/and have higher precedence than if unless while until modifiers" do
|
||||
(1 if 2 and 3).should == 1
|
||||
(1 if 2 or 3).should == 1
|
||||
|
||||
(1 unless false and true).should == 1
|
||||
(1 unless false or false).should == 1
|
||||
|
||||
(1 while true and false).should == nil # would hang upon error
|
||||
(1 while false or false).should == nil
|
||||
|
||||
((raise until true and false) rescue 10).should == 10
|
||||
(1 until false or true).should == nil # would hang upon error
|
||||
end
|
||||
|
||||
# XXX: it seems to me they are right-associative
|
||||
it "if unless while until are non-associative"
|
||||
end
|
29
spec/ruby/language/predefined/data_spec.rb
Normal file
29
spec/ruby/language/predefined/data_spec.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "The DATA constant" do
|
||||
it "exists when the main script contains __END__" do
|
||||
ruby_exe(fixture(__FILE__, "data1.rb")).chomp.should == "true"
|
||||
end
|
||||
|
||||
it "does not exist when the main script contains no __END__" do
|
||||
ruby_exe("puts Object.const_defined?(:DATA)").chomp.should == 'false'
|
||||
end
|
||||
|
||||
it "does not exist when an included file has a __END__" do
|
||||
ruby_exe(fixture(__FILE__, "data2.rb")).chomp.should == "false"
|
||||
end
|
||||
|
||||
it "does not change when an included files also has a __END__" do
|
||||
ruby_exe(fixture(__FILE__, "data3.rb")).chomp.should == "data 3"
|
||||
end
|
||||
|
||||
it "is included in an otherwise empty file" do
|
||||
ap = fixture(__FILE__, "print_data.rb")
|
||||
str = ruby_exe(fixture(__FILE__, "data_only.rb"), options: "-r#{ap}")
|
||||
str.chomp.should == "data only"
|
||||
end
|
||||
|
||||
it "rewinds to the head of the main script" do
|
||||
ruby_exe(fixture(__FILE__, "data5.rb")).chomp.should == "DATA.rewind"
|
||||
end
|
||||
end
|
4
spec/ruby/language/predefined/fixtures/data1.rb
Normal file
4
spec/ruby/language/predefined/fixtures/data1.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
puts Object.const_defined?(:DATA)
|
||||
|
||||
__END__
|
||||
data1
|
4
spec/ruby/language/predefined/fixtures/data2.rb
Normal file
4
spec/ruby/language/predefined/fixtures/data2.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
require File.expand_path("../data4.rb", __FILE__)
|
||||
|
||||
p Object.const_defined?(:DATA)
|
7
spec/ruby/language/predefined/fixtures/data3.rb
Normal file
7
spec/ruby/language/predefined/fixtures/data3.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
require File.expand_path("../data4.rb", __FILE__)
|
||||
|
||||
puts DATA.read
|
||||
|
||||
__END__
|
||||
data 3
|
4
spec/ruby/language/predefined/fixtures/data4.rb
Normal file
4
spec/ruby/language/predefined/fixtures/data4.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
# nothing
|
||||
|
||||
__END__
|
||||
data 4
|
5
spec/ruby/language/predefined/fixtures/data5.rb
Normal file
5
spec/ruby/language/predefined/fixtures/data5.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
DATA.rewind
|
||||
puts DATA.gets
|
||||
|
||||
__END__
|
||||
data 5
|
2
spec/ruby/language/predefined/fixtures/data_only.rb
Normal file
2
spec/ruby/language/predefined/fixtures/data_only.rb
Normal file
|
@ -0,0 +1,2 @@
|
|||
__END__
|
||||
data only
|
3
spec/ruby/language/predefined/fixtures/print_data.rb
Normal file
3
spec/ruby/language/predefined/fixtures/print_data.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
at_exit {
|
||||
puts DATA.read
|
||||
}
|
1221
spec/ruby/language/predefined_spec.rb
Normal file
1221
spec/ruby/language/predefined_spec.rb
Normal file
File diff suppressed because it is too large
Load diff
67
spec/ruby/language/private_spec.rb
Normal file
67
spec/ruby/language/private_spec.rb
Normal file
|
@ -0,0 +1,67 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/private', __FILE__)
|
||||
|
||||
describe "The private keyword" do
|
||||
it "marks following methods as being private" do
|
||||
a = Private::A.new
|
||||
a.methods.should_not include(:bar)
|
||||
lambda { a.bar }.should raise_error(NoMethodError)
|
||||
|
||||
b = Private::B.new
|
||||
b.methods.should_not include(:bar)
|
||||
lambda { b.bar }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
# def expr.meth() methods are always public
|
||||
it "has no effect on def expr.meth() methods" do
|
||||
Private::B.public_defs_method.should == 0
|
||||
end
|
||||
|
||||
it "is overridden when a new class is opened" do
|
||||
c = Private::B::C.new
|
||||
c.methods.should include(:baz)
|
||||
c.baz
|
||||
Private::B.public_class_method1.should == 1
|
||||
lambda { Private::B.private_class_method1 }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "is no longer in effect when the class is closed" do
|
||||
b = Private::B.new
|
||||
b.methods.should include(:foo)
|
||||
b.foo
|
||||
end
|
||||
|
||||
it "changes visibility of previously called method" do
|
||||
klass = Class.new do
|
||||
def foo
|
||||
"foo"
|
||||
end
|
||||
end
|
||||
f = klass.new
|
||||
f.foo
|
||||
klass.class_eval do
|
||||
private :foo
|
||||
end
|
||||
lambda { f.foo }.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "changes visiblity of previously called methods with same send/call site" do
|
||||
g = ::Private::G.new
|
||||
lambda {
|
||||
2.times do
|
||||
g.foo
|
||||
module ::Private
|
||||
class G
|
||||
private :foo
|
||||
end
|
||||
end
|
||||
end
|
||||
}.should raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "changes the visibility of the existing method in the subclass" do
|
||||
::Private::A.new.foo.should == 'foo'
|
||||
lambda {::Private::H.new.foo}.should raise_error(NoMethodError)
|
||||
::Private::H.new.send(:foo).should == 'foo'
|
||||
end
|
||||
end
|
220
spec/ruby/language/proc_spec.rb
Normal file
220
spec/ruby/language/proc_spec.rb
Normal file
|
@ -0,0 +1,220 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "A Proc" do
|
||||
it "captures locals from the surrounding scope" do
|
||||
var = 1
|
||||
lambda { var }.call.should == 1
|
||||
end
|
||||
|
||||
it "does not capture a local when an argument has the same name" do
|
||||
var = 1
|
||||
lambda { |var| var }.call(2).should == 2
|
||||
var.should == 1
|
||||
end
|
||||
|
||||
describe "taking zero arguments" do
|
||||
before :each do
|
||||
@l = lambda { 1 }
|
||||
end
|
||||
|
||||
it "does not raise an exception if no values are passed" do
|
||||
@l.call.should == 1
|
||||
end
|
||||
|
||||
it "raises an ArgumentErro if a value is passed" do
|
||||
lambda { @l.call(0) }.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking || arguments" do
|
||||
before :each do
|
||||
@l = lambda { || 1 }
|
||||
end
|
||||
|
||||
it "does not raise an exception when passed no values" do
|
||||
@l.call.should == 1
|
||||
end
|
||||
|
||||
it "raises an ArgumentError if a value is passed" do
|
||||
lambda { @l.call(0) }.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |a| arguments" do
|
||||
before :each do
|
||||
@l = lambda { |a| a }
|
||||
end
|
||||
|
||||
it "assigns the value passed to the argument" do
|
||||
@l.call(2).should == 2
|
||||
end
|
||||
|
||||
it "does not destructure a single Array value" do
|
||||
@l.call([1, 2]).should == [1, 2]
|
||||
end
|
||||
|
||||
it "does not call #to_ary to convert a single passed object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@l.call(obj).should equal(obj)
|
||||
end
|
||||
|
||||
it "raises an ArgumentError if no value is passed" do
|
||||
lambda { @l.call }.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |a, b| arguments" do
|
||||
before :each do
|
||||
@l = lambda { |a, b| [a, b] }
|
||||
end
|
||||
|
||||
it "raises an ArgumentError if passed no values" do
|
||||
lambda { @l.call }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "raises an ArgumentError if passed one value" do
|
||||
lambda { @l.call(0) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "assigns the values passed to the arguments" do
|
||||
@l.call(1, 2).should == [1, 2]
|
||||
end
|
||||
|
||||
it "does not call #to_ary to convert a single passed object to an Array" do
|
||||
obj = mock("proc call to_ary")
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
lambda { @l.call(obj) }.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |a, *b| arguments" do
|
||||
before :each do
|
||||
@l = lambda { |a, *b| [a, b] }
|
||||
end
|
||||
|
||||
it "raises an ArgumentError if passed no values" do
|
||||
lambda { @l.call }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "does not destructure a single Array value yielded" do
|
||||
@l.call([1, 2, 3]).should == [[1, 2, 3], []]
|
||||
end
|
||||
|
||||
it "assigns all passed values after the first to the rest argument" do
|
||||
@l.call(1, 2, 3).should == [1, [2, 3]]
|
||||
end
|
||||
|
||||
it "does not call #to_ary to convert a single passed object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@l.call(obj).should == [obj, []]
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |*| arguments" do
|
||||
before :each do
|
||||
@l = lambda { |*| 1 }
|
||||
end
|
||||
|
||||
it "does not raise an exception when passed no values" do
|
||||
@l.call.should == 1
|
||||
end
|
||||
|
||||
it "does not raise an exception when passed multiple values" do
|
||||
@l.call(2, 3, 4).should == 1
|
||||
end
|
||||
|
||||
it "does not call #to_ary to convert a single passed object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@l.call(obj).should == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |*a| arguments" do
|
||||
before :each do
|
||||
@l = lambda { |*a| a }
|
||||
end
|
||||
|
||||
it "assigns [] to the argument when passed no values" do
|
||||
@l.call.should == []
|
||||
end
|
||||
|
||||
it "assigns the argument an Array wrapping one passed value" do
|
||||
@l.call(1).should == [1]
|
||||
end
|
||||
|
||||
it "assigns the argument an Array wrapping all values passed" do
|
||||
@l.call(1, 2, 3).should == [1, 2, 3]
|
||||
end
|
||||
|
||||
it "does not call #to_ary to convert a single passed object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@l.call(obj).should == [obj]
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |a, | arguments" do
|
||||
before :each do
|
||||
@l = lambda { |a, | a }
|
||||
end
|
||||
|
||||
it "raises an ArgumentError when passed no values" do
|
||||
lambda { @l.call }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "raises an ArgumentError when passed more than one value" do
|
||||
lambda { @l.call(1, 2) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "assigns the argument the value passed" do
|
||||
@l.call(1).should == 1
|
||||
end
|
||||
|
||||
it "does not destructure when passed a single Array" do
|
||||
@l.call([1,2]).should == [1, 2]
|
||||
end
|
||||
|
||||
it "does not call #to_ary to convert a single passed object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_not_receive(:to_ary)
|
||||
|
||||
@l.call(obj).should == obj
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |(a, b)| arguments" do
|
||||
before :each do
|
||||
@l = lambda { |(a, b)| [a, b] }
|
||||
end
|
||||
|
||||
it "raises an ArgumentError when passed no values" do
|
||||
lambda { @l.call }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "destructures a single Array value yielded" do
|
||||
@l.call([1, 2]).should == [1, 2]
|
||||
end
|
||||
|
||||
it "calls #to_ary to convert a single passed object to an Array" do
|
||||
obj = mock("block yield to_ary")
|
||||
obj.should_receive(:to_ary).and_return([1, 2])
|
||||
|
||||
@l.call(obj).should == [1, 2]
|
||||
end
|
||||
|
||||
it "raises a TypeError if #to_ary does not return an Array" do
|
||||
obj = mock("block yield to_ary invalid")
|
||||
obj.should_receive(:to_ary).and_return(1)
|
||||
|
||||
lambda { @l.call(obj) }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
end
|
66
spec/ruby/language/redo_spec.rb
Normal file
66
spec/ruby/language/redo_spec.rb
Normal file
|
@ -0,0 +1,66 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
|
||||
describe "The redo statement" do
|
||||
it "restarts block execution if used within block" do
|
||||
a = []
|
||||
lambda {
|
||||
a << 1
|
||||
redo if a.size < 2
|
||||
a << 2
|
||||
}.call
|
||||
a.should == [1, 1, 2]
|
||||
end
|
||||
|
||||
it "re-executes the closest loop" do
|
||||
exist = [2,3]
|
||||
processed = []
|
||||
order = []
|
||||
[1,2,3,4].each do |x|
|
||||
order << x
|
||||
begin
|
||||
processed << x
|
||||
if exist.include?(x)
|
||||
raise StandardError, "included"
|
||||
end
|
||||
rescue StandardError
|
||||
exist.delete(x)
|
||||
redo
|
||||
end
|
||||
end
|
||||
processed.should == [1,2,2,3,3,4]
|
||||
exist.should == []
|
||||
order.should == [1,2,2,3,3,4]
|
||||
end
|
||||
|
||||
it "re-executes the last step in enumeration" do
|
||||
list = []
|
||||
[1,2,3].each do |x|
|
||||
list << x
|
||||
break if list.size == 6
|
||||
redo if x == 3
|
||||
end
|
||||
list.should == [1,2,3,3,3,3]
|
||||
end
|
||||
|
||||
it "triggers ensure block when re-executing a block" do
|
||||
list = []
|
||||
[1,2,3].each do |x|
|
||||
list << x
|
||||
begin
|
||||
list << 10*x
|
||||
redo if list.count(1) == 1
|
||||
ensure
|
||||
list << 100*x
|
||||
end
|
||||
end
|
||||
list.should == [1,10,100,1,10,100,2,20,200,3,30,300]
|
||||
end
|
||||
|
||||
describe "in a method" do
|
||||
it "is invalid and raises a SyntaxError" do
|
||||
lambda {
|
||||
eval("def m; redo; end")
|
||||
}.should raise_error(SyntaxError)
|
||||
end
|
||||
end
|
||||
end
|
179
spec/ruby/language/regexp/anchors_spec.rb
Normal file
179
spec/ruby/language/regexp/anchors_spec.rb
Normal file
|
@ -0,0 +1,179 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Regexps with anchors" do
|
||||
it "supports ^ (line start anchor)" do
|
||||
# Basic matching
|
||||
/^foo/.match("foo").to_a.should == ["foo"]
|
||||
/^bar/.match("foo\nbar").to_a.should == ["bar"]
|
||||
# Basic non-matching
|
||||
/^foo/.match(" foo").should be_nil
|
||||
/foo^/.match("foo\n\n\n").should be_nil
|
||||
|
||||
# A bit advanced
|
||||
/^^^foo/.match("foo").to_a.should == ["foo"]
|
||||
(/^[^f]/ =~ "foo\n\n").should == "foo\n".size and $~.to_a.should == ["\n"]
|
||||
(/($^)($^)/ =~ "foo\n\n").should == "foo\n".size and $~.to_a.should == ["", "", ""]
|
||||
|
||||
# Different start of line chars
|
||||
/^bar/.match("foo\rbar").should be_nil
|
||||
/^bar/.match("foo\0bar").should be_nil
|
||||
|
||||
# Trivial
|
||||
/^/.match("foo").to_a.should == [""]
|
||||
|
||||
# Grouping
|
||||
/(^foo)/.match("foo").to_a.should == ["foo", "foo"]
|
||||
/(^)/.match("foo").to_a.should == ["", ""]
|
||||
/(foo\n^)(^bar)/.match("foo\nbar").to_a.should == ["foo\nbar", "foo\n", "bar"]
|
||||
end
|
||||
|
||||
it "does not match ^ after trailing \\n" do
|
||||
/^(?!\A)/.match("foo\n").should be_nil # There is no (empty) line after a trailing \n
|
||||
end
|
||||
|
||||
it "supports $ (line end anchor)" do
|
||||
# Basic matching
|
||||
/foo$/.match("foo").to_a.should == ["foo"]
|
||||
/foo$/.match("foo\nbar").to_a.should == ["foo"]
|
||||
# Basic non-matching
|
||||
/foo$/.match("foo ").should be_nil
|
||||
/$foo/.match("\n\n\nfoo").should be_nil
|
||||
|
||||
# A bit advanced
|
||||
/foo$$$/.match("foo").to_a.should == ["foo"]
|
||||
(/[^o]$/ =~ "foo\n\n").should == ("foo\n".size - 1) and $~.to_a.should == ["\n"]
|
||||
|
||||
# Different end of line chars
|
||||
/foo$/.match("foo\r\nbar").should be_nil
|
||||
/foo$/.match("foo\0bar").should be_nil
|
||||
|
||||
# Trivial
|
||||
(/$/ =~ "foo").should == "foo".size and $~.to_a.should == [""]
|
||||
|
||||
# Grouping
|
||||
/(foo$)/.match("foo").to_a.should == ["foo", "foo"]
|
||||
(/($)/ =~ "foo").should == "foo".size and $~.to_a.should == ["", ""]
|
||||
/(foo$)($\nbar)/.match("foo\nbar").to_a.should == ["foo\nbar", "foo", "\nbar"]
|
||||
end
|
||||
|
||||
it "supports \\A (string start anchor)" do
|
||||
# Basic matching
|
||||
/\Afoo/.match("foo").to_a.should == ["foo"]
|
||||
# Basic non-matching
|
||||
/\Abar/.match("foo\nbar").should be_nil
|
||||
/\Afoo/.match(" foo").should be_nil
|
||||
|
||||
# A bit advanced
|
||||
/\A\A\Afoo/.match("foo").to_a.should == ["foo"]
|
||||
/(\A\Z)(\A\Z)/.match("").to_a.should == ["", "", ""]
|
||||
|
||||
# Different start of line chars
|
||||
/\Abar/.match("foo\0bar").should be_nil
|
||||
|
||||
# Grouping
|
||||
/(\Afoo)/.match("foo").to_a.should == ["foo", "foo"]
|
||||
/(\A)/.match("foo").to_a.should == ["", ""]
|
||||
end
|
||||
|
||||
it "supports \\Z (string end anchor, including before trailing \\n)" do
|
||||
# Basic matching
|
||||
/foo\Z/.match("foo").to_a.should == ["foo"]
|
||||
/foo\Z/.match("foo\n").to_a.should == ["foo"]
|
||||
# Basic non-matching
|
||||
/foo\Z/.match("foo\nbar").should be_nil
|
||||
/foo\Z/.match("foo ").should be_nil
|
||||
|
||||
# A bit advanced
|
||||
/foo\Z\Z\Z/.match("foo\n").to_a.should == ["foo"]
|
||||
(/($\Z)($\Z)/ =~ "foo\n").should == "foo".size and $~.to_a.should == ["", "", ""]
|
||||
(/(\z\Z)(\z\Z)/ =~ "foo\n").should == "foo\n".size and $~.to_a.should == ["", "", ""]
|
||||
|
||||
# Different end of line chars
|
||||
/foo\Z/.match("foo\0bar").should be_nil
|
||||
/foo\Z/.match("foo\r\n").should be_nil
|
||||
|
||||
# Grouping
|
||||
/(foo\Z)/.match("foo").to_a.should == ["foo", "foo"]
|
||||
(/(\Z)/ =~ "foo").should == "foo".size and $~.to_a.should == ["", ""]
|
||||
end
|
||||
|
||||
it "supports \\z (string end anchor)" do
|
||||
# Basic matching
|
||||
/foo\z/.match("foo").to_a.should == ["foo"]
|
||||
# Basic non-matching
|
||||
/foo\z/.match("foo\nbar").should be_nil
|
||||
/foo\z/.match("foo\n").should be_nil
|
||||
/foo\z/.match("foo ").should be_nil
|
||||
|
||||
# A bit advanced
|
||||
/foo\z\z\z/.match("foo").to_a.should == ["foo"]
|
||||
(/($\z)($\z)/ =~ "foo").should == "foo".size and $~.to_a.should == ["", "", ""]
|
||||
|
||||
# Different end of line chars
|
||||
/foo\z/.match("foo\0bar").should be_nil
|
||||
/foo\z/.match("foo\r\nbar").should be_nil
|
||||
|
||||
# Grouping
|
||||
/(foo\z)/.match("foo").to_a.should == ["foo", "foo"]
|
||||
(/(\z)/ =~ "foo").should == "foo".size and $~.to_a.should == ["", ""]
|
||||
end
|
||||
|
||||
it "supports \\b (word boundary)" do
|
||||
# Basic matching
|
||||
/foo\b/.match("foo").to_a.should == ["foo"]
|
||||
/foo\b/.match("foo\n").to_a.should == ["foo"]
|
||||
LanguageSpecs.white_spaces.scan(/./).each do |c|
|
||||
/foo\b/.match("foo" + c).to_a.should == ["foo"]
|
||||
end
|
||||
LanguageSpecs.non_alphanum_non_space.scan(/./).each do |c|
|
||||
/foo\b/.match("foo" + c).to_a.should == ["foo"]
|
||||
end
|
||||
/foo\b/.match("foo\0").to_a.should == ["foo"]
|
||||
# Basic non-matching
|
||||
/foo\b/.match("foobar").should be_nil
|
||||
/foo\b/.match("foo123").should be_nil
|
||||
/foo\b/.match("foo_").should be_nil
|
||||
end
|
||||
|
||||
it "supports \\B (non-word-boundary)" do
|
||||
# Basic matching
|
||||
/foo\B/.match("foobar").to_a.should == ["foo"]
|
||||
/foo\B/.match("foo123").to_a.should == ["foo"]
|
||||
/foo\B/.match("foo_").to_a.should == ["foo"]
|
||||
# Basic non-matching
|
||||
/foo\B/.match("foo").should be_nil
|
||||
/foo\B/.match("foo\n").should be_nil
|
||||
LanguageSpecs.white_spaces.scan(/./).each do |c|
|
||||
/foo\B/.match("foo" + c).should be_nil
|
||||
end
|
||||
LanguageSpecs.non_alphanum_non_space.scan(/./).each do |c|
|
||||
/foo\B/.match("foo" + c).should be_nil
|
||||
end
|
||||
/foo\B/.match("foo\0").should be_nil
|
||||
end
|
||||
|
||||
it "supports (?= ) (positive lookahead)" do
|
||||
/foo.(?=bar)/.match("foo1 foo2bar").to_a.should == ["foo2"]
|
||||
end
|
||||
|
||||
it "supports (?! ) (negative lookahead)" do
|
||||
/foo.(?!bar)/.match("foo1bar foo2").to_a.should == ["foo2"]
|
||||
end
|
||||
|
||||
it "supports (?!<) (negative lookbehind)" do
|
||||
/(?<!foo)bar./.match("foobar1 bar2").to_a.should == ["bar2"]
|
||||
end
|
||||
|
||||
it "supports (?<=) (positive lookbehind)" do
|
||||
/(?<=foo)bar./.match("bar1 foobar2").to_a.should == ["bar2"]
|
||||
end
|
||||
|
||||
it "supports (?<=\\b) (positive lookbehind with word boundary)" do
|
||||
/(?<=\bfoo)bar./.match("1foobar1 foobar2").to_a.should == ["bar2"]
|
||||
end
|
||||
|
||||
it "supports (?!<\\b) (negative lookbehind with word boundary)" do
|
||||
/(?<!\bfoo)bar./.match("foobar1 1foobar2").to_a.should == ["bar2"]
|
||||
end
|
||||
end
|
48
spec/ruby/language/regexp/back-references_spec.rb
Normal file
48
spec/ruby/language/regexp/back-references_spec.rb
Normal file
|
@ -0,0 +1,48 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Regexps with back-references" do
|
||||
it "saves match data in the $~ pseudo-global variable" do
|
||||
"hello" =~ /l+/
|
||||
$~.to_a.should == ["ll"]
|
||||
end
|
||||
|
||||
it "saves captures in numbered $[1-9] variables" do
|
||||
"1234567890" =~ /(1)(2)(3)(4)(5)(6)(7)(8)(9)(0)/
|
||||
$~.to_a.should == ["1234567890", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
|
||||
$1.should == "1"
|
||||
$2.should == "2"
|
||||
$3.should == "3"
|
||||
$4.should == "4"
|
||||
$5.should == "5"
|
||||
$6.should == "6"
|
||||
$7.should == "7"
|
||||
$8.should == "8"
|
||||
$9.should == "9"
|
||||
end
|
||||
|
||||
it "will not clobber capture variables across threads" do
|
||||
cap1, cap2, cap3 = nil
|
||||
"foo" =~ /(o+)/
|
||||
cap1 = [$~.to_a, $1]
|
||||
Thread.new do
|
||||
cap2 = [$~.to_a, $1]
|
||||
"bar" =~ /(a)/
|
||||
cap3 = [$~.to_a, $1]
|
||||
end.join
|
||||
cap4 = [$~.to_a, $1]
|
||||
cap1.should == [["oo", "oo"], "oo"]
|
||||
cap2.should == [[], nil]
|
||||
cap3.should == [["a", "a"], "a"]
|
||||
cap4.should == [["oo", "oo"], "oo"]
|
||||
end
|
||||
|
||||
it "supports \<n> (backreference to previous group match)" do
|
||||
/(foo.)\1/.match("foo1foo1").to_a.should == ["foo1foo1", "foo1"]
|
||||
/(foo.)\1/.match("foo1foo2").should be_nil
|
||||
end
|
||||
|
||||
it "resets nested \<n> backreference before match of outer subexpression" do
|
||||
/(a\1?){2}/.match("aaaa").to_a.should == ["aa", "a"]
|
||||
end
|
||||
end
|
610
spec/ruby/language/regexp/character_classes_spec.rb
Normal file
610
spec/ruby/language/regexp/character_classes_spec.rb
Normal file
|
@ -0,0 +1,610 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Regexp with character classes" do
|
||||
it "supports \\w (word character)" do
|
||||
/\w/.match("a").to_a.should == ["a"]
|
||||
/\w/.match("1").to_a.should == ["1"]
|
||||
/\w/.match("_").to_a.should == ["_"]
|
||||
|
||||
# Non-matches
|
||||
/\w/.match(LanguageSpecs.white_spaces).should be_nil
|
||||
/\w/.match(LanguageSpecs.non_alphanum_non_space).should be_nil
|
||||
/\w/.match("\0").should be_nil
|
||||
end
|
||||
|
||||
it "supports \\W (non-word character)" do
|
||||
/\W+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.white_spaces]
|
||||
/\W+/.match(LanguageSpecs.non_alphanum_non_space).to_a.should == [LanguageSpecs.non_alphanum_non_space]
|
||||
/\W/.match("\0").to_a.should == ["\0"]
|
||||
|
||||
# Non-matches
|
||||
/\W/.match("a").should be_nil
|
||||
/\W/.match("1").should be_nil
|
||||
/\W/.match("_").should be_nil
|
||||
end
|
||||
|
||||
it "supports \\s (space character)" do
|
||||
/\s+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.white_spaces]
|
||||
|
||||
# Non-matches
|
||||
/\s/.match("a").should be_nil
|
||||
/\s/.match("1").should be_nil
|
||||
/\s/.match(LanguageSpecs.non_alphanum_non_space).should be_nil
|
||||
/\s/.match("\0").should be_nil
|
||||
end
|
||||
|
||||
it "supports \\S (non-space character)" do
|
||||
/\S/.match("a").to_a.should == ["a"]
|
||||
/\S/.match("1").to_a.should == ["1"]
|
||||
/\S+/.match(LanguageSpecs.non_alphanum_non_space).to_a.should == [LanguageSpecs.non_alphanum_non_space]
|
||||
/\S/.match("\0").to_a.should == ["\0"]
|
||||
|
||||
# Non-matches
|
||||
/\S/.match(LanguageSpecs.white_spaces).should be_nil
|
||||
end
|
||||
|
||||
it "supports \\d (numeric digit)" do
|
||||
/\d/.match("1").to_a.should == ["1"]
|
||||
|
||||
# Non-matches
|
||||
/\d/.match("a").should be_nil
|
||||
/\d/.match(LanguageSpecs.white_spaces).should be_nil
|
||||
/\d/.match(LanguageSpecs.non_alphanum_non_space).should be_nil
|
||||
/\d/.match("\0").should be_nil
|
||||
end
|
||||
|
||||
it "supports \\D (non-digit)" do
|
||||
/\D/.match("a").to_a.should == ["a"]
|
||||
/\D+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.white_spaces]
|
||||
/\D+/.match(LanguageSpecs.non_alphanum_non_space).to_a.should == [LanguageSpecs.non_alphanum_non_space]
|
||||
/\D/.match("\0").to_a.should == ["\0"]
|
||||
|
||||
# Non-matches
|
||||
/\D/.match("1").should be_nil
|
||||
end
|
||||
|
||||
it "supports [] (character class)" do
|
||||
/[a-z]+/.match("fooBAR").to_a.should == ["foo"]
|
||||
/[\b]/.match("\b").to_a.should == ["\b"] # \b inside character class is backspace
|
||||
end
|
||||
|
||||
it "supports [[:alpha:][:digit:][:etc:]] (predefined character classes)" do
|
||||
/[[:alnum:]]+/.match("a1").to_a.should == ["a1"]
|
||||
/[[:alpha:]]+/.match("Aa1").to_a.should == ["Aa"]
|
||||
/[[:blank:]]+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.blanks]
|
||||
# /[[:cntrl:]]/.match("").to_a.should == [""] # TODO: what should this match?
|
||||
/[[:digit:]]/.match("1").to_a.should == ["1"]
|
||||
# /[[:graph:]]/.match("").to_a.should == [""] # TODO: what should this match?
|
||||
/[[:lower:]]+/.match("Aa1").to_a.should == ["a"]
|
||||
/[[:print:]]+/.match(LanguageSpecs.white_spaces).to_a.should == [" "] # include all of multibyte encoded characters
|
||||
/[[:punct:]]+/.match(LanguageSpecs.punctuations).to_a.should == [LanguageSpecs.punctuations]
|
||||
/[[:space:]]+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.white_spaces]
|
||||
/[[:upper:]]+/.match("123ABCabc").to_a.should == ["ABC"]
|
||||
/[[:xdigit:]]+/.match("xyz0123456789ABCDEFabcdefXYZ").to_a.should == ["0123456789ABCDEFabcdef"]
|
||||
|
||||
# Parsing
|
||||
/[[:lower:][:digit:]A-C]+/.match("a1ABCDEF").to_a.should == ["a1ABC"] # can be composed with other constructs in the character class
|
||||
/[^[:lower:]A-C]+/.match("abcABCDEF123def").to_a.should == ["DEF123"] # negated character class
|
||||
/[:alnum:]+/.match("a:l:n:u:m").to_a.should == ["a:l:n:u:m"] # should behave like regular character class composed of the individual letters
|
||||
/[\[:alnum:]+/.match("[:a:l:n:u:m").to_a.should == ["[:a:l:n:u:m"] # should behave like regular character class composed of the individual letters
|
||||
lambda { eval('/[[:alpha:]-[:digit:]]/') }.should raise_error(SyntaxError) # can't use character class as a start value of range
|
||||
end
|
||||
|
||||
it "matches ASCII characters with [[:ascii:]]" do
|
||||
"\x00".match(/[[:ascii:]]/).to_a.should == ["\x00"]
|
||||
"\x7F".match(/[[:ascii:]]/).to_a.should == ["\x7F"]
|
||||
end
|
||||
|
||||
not_supported_on :opal do
|
||||
it "doesn't match non-ASCII characters with [[:ascii:]]" do
|
||||
/[[:ascii:]]/.match("\u{80}").should be_nil
|
||||
/[[:ascii:]]/.match("\u{9898}").should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it "matches Unicode letter characters with [[:alnum:]]" do
|
||||
"à".match(/[[:alnum:]]/).to_a.should == ["à"]
|
||||
end
|
||||
|
||||
it "matches Unicode digits with [[:alnum:]]" do
|
||||
"\u{0660}".match(/[[:alnum:]]/).to_a.should == ["\u{0660}"]
|
||||
end
|
||||
|
||||
it "doesn't matches Unicode marks with [[:alnum:]]" do
|
||||
"\u{36F}".match(/[[:alnum:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode control characters with [[:alnum:]]" do
|
||||
"\u{16}".match(/[[:alnum:]]/).to_a.should == []
|
||||
end
|
||||
|
||||
it "doesn't match Unicode punctuation characters with [[:alnum:]]" do
|
||||
"\u{3F}".match(/[[:alnum:]]/).to_a.should == []
|
||||
end
|
||||
|
||||
it "matches Unicode letter characters with [[:alpha:]]" do
|
||||
"à".match(/[[:alpha:]]/).to_a.should == ["à"]
|
||||
end
|
||||
|
||||
it "doesn't match Unicode digits with [[:alpha:]]" do
|
||||
"\u{0660}".match(/[[:alpha:]]/).to_a.should == []
|
||||
end
|
||||
|
||||
it "doesn't matches Unicode marks with [[:alpha:]]" do
|
||||
"\u{36F}".match(/[[:alpha:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode control characters with [[:alpha:]]" do
|
||||
"\u{16}".match(/[[:alpha:]]/).to_a.should == []
|
||||
end
|
||||
|
||||
it "doesn't match Unicode punctuation characters with [[:alpha:]]" do
|
||||
"\u{3F}".match(/[[:alpha:]]/).to_a.should == []
|
||||
end
|
||||
|
||||
it "matches Unicode space characters with [[:blank:]]" do
|
||||
"\u{1680}".match(/[[:blank:]]/).to_a.should == ["\u{1680}"]
|
||||
end
|
||||
|
||||
it "doesn't match Unicode control characters with [[:blank:]]" do
|
||||
"\u{16}".match(/[[:blank:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode punctuation characters with [[:blank:]]" do
|
||||
"\u{3F}".match(/[[:blank:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode letter characters with [[:blank:]]" do
|
||||
"à".match(/[[:blank:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode digits with [[:blank:]]" do
|
||||
"\u{0660}".match(/[[:blank:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode marks with [[:blank:]]" do
|
||||
"\u{36F}".match(/[[:blank:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't Unicode letter characters with [[:cntrl:]]" do
|
||||
"à".match(/[[:cntrl:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode digits with [[:cntrl:]]" do
|
||||
"\u{0660}".match(/[[:cntrl:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode marks with [[:cntrl:]]" do
|
||||
"\u{36F}".match(/[[:cntrl:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode punctuation characters with [[:cntrl:]]" do
|
||||
"\u{3F}".match(/[[:cntrl:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "matches Unicode control characters with [[:cntrl:]]" do
|
||||
"\u{16}".match(/[[:cntrl:]]/).to_a.should == ["\u{16}"]
|
||||
end
|
||||
|
||||
it "doesn't match Unicode format characters with [[:cntrl:]]" do
|
||||
"\u{2060}".match(/[[:cntrl:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode private-use characters with [[:cntrl:]]" do
|
||||
"\u{E001}".match(/[[:cntrl:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode letter characters with [[:digit:]]" do
|
||||
"à".match(/[[:digit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "matches Unicode digits with [[:digit:]]" do
|
||||
"\u{0660}".match(/[[:digit:]]/).to_a.should == ["\u{0660}"]
|
||||
"\u{FF12}".match(/[[:digit:]]/).to_a.should == ["\u{FF12}"]
|
||||
end
|
||||
|
||||
it "doesn't match Unicode marks with [[:digit:]]" do
|
||||
"\u{36F}".match(/[[:digit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode punctuation characters with [[:digit:]]" do
|
||||
"\u{3F}".match(/[[:digit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode control characters with [[:digit:]]" do
|
||||
"\u{16}".match(/[[:digit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode format characters with [[:digit:]]" do
|
||||
"\u{2060}".match(/[[:digit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode private-use characters with [[:digit:]]" do
|
||||
"\u{E001}".match(/[[:digit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "matches Unicode letter characters with [[:graph:]]" do
|
||||
"à".match(/[[:graph:]]/).to_a.should == ["à"]
|
||||
end
|
||||
|
||||
it "matches Unicode digits with [[:graph:]]" do
|
||||
"\u{0660}".match(/[[:graph:]]/).to_a.should == ["\u{0660}"]
|
||||
"\u{FF12}".match(/[[:graph:]]/).to_a.should == ["\u{FF12}"]
|
||||
end
|
||||
|
||||
it "matches Unicode marks with [[:graph:]]" do
|
||||
"\u{36F}".match(/[[:graph:]]/).to_a.should ==["\u{36F}"]
|
||||
end
|
||||
|
||||
it "matches Unicode punctuation characters with [[:graph:]]" do
|
||||
"\u{3F}".match(/[[:graph:]]/).to_a.should == ["\u{3F}"]
|
||||
end
|
||||
|
||||
it "doesn't match Unicode control characters with [[:graph:]]" do
|
||||
"\u{16}".match(/[[:graph:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "match Unicode format characters with [[:graph:]]" do
|
||||
"\u{2060}".match(/[[:graph:]]/).to_a.should == ["\u2060"]
|
||||
end
|
||||
|
||||
it "match Unicode private-use characters with [[:graph:]]" do
|
||||
"\u{E001}".match(/[[:graph:]]/).to_a.should == ["\u{E001}"]
|
||||
end
|
||||
|
||||
it "matches Unicode lowercase letter characters with [[:lower:]]" do
|
||||
"\u{FF41}".match(/[[:lower:]]/).to_a.should == ["\u{FF41}"]
|
||||
"\u{1D484}".match(/[[:lower:]]/).to_a.should == ["\u{1D484}"]
|
||||
"\u{E8}".match(/[[:lower:]]/).to_a.should == ["\u{E8}"]
|
||||
end
|
||||
|
||||
it "doesn't match Unicode uppercase letter characters with [[:lower:]]" do
|
||||
"\u{100}".match(/[[:lower:]]/).should be_nil
|
||||
"\u{130}".match(/[[:lower:]]/).should be_nil
|
||||
"\u{405}".match(/[[:lower:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode title-case characters with [[:lower:]]" do
|
||||
"\u{1F88}".match(/[[:lower:]]/).should be_nil
|
||||
"\u{1FAD}".match(/[[:lower:]]/).should be_nil
|
||||
"\u{01C5}".match(/[[:lower:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode digits with [[:lower:]]" do
|
||||
"\u{0660}".match(/[[:lower:]]/).should be_nil
|
||||
"\u{FF12}".match(/[[:lower:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode marks with [[:lower:]]" do
|
||||
"\u{36F}".match(/[[:lower:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode punctuation characters with [[:lower:]]" do
|
||||
"\u{3F}".match(/[[:lower:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode control characters with [[:lower:]]" do
|
||||
"\u{16}".match(/[[:lower:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode format characters with [[:lower:]]" do
|
||||
"\u{2060}".match(/[[:lower:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode private-use characters with [[:lower:]]" do
|
||||
"\u{E001}".match(/[[:lower:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "matches Unicode lowercase letter characters with [[:print:]]" do
|
||||
"\u{FF41}".match(/[[:print:]]/).to_a.should == ["\u{FF41}"]
|
||||
"\u{1D484}".match(/[[:print:]]/).to_a.should == ["\u{1D484}"]
|
||||
"\u{E8}".match(/[[:print:]]/).to_a.should == ["\u{E8}"]
|
||||
end
|
||||
|
||||
it "matches Unicode uppercase letter characters with [[:print:]]" do
|
||||
"\u{100}".match(/[[:print:]]/).to_a.should == ["\u{100}"]
|
||||
"\u{130}".match(/[[:print:]]/).to_a.should == ["\u{130}"]
|
||||
"\u{405}".match(/[[:print:]]/).to_a.should == ["\u{405}"]
|
||||
end
|
||||
|
||||
it "matches Unicode title-case characters with [[:print:]]" do
|
||||
"\u{1F88}".match(/[[:print:]]/).to_a.should == ["\u{1F88}"]
|
||||
"\u{1FAD}".match(/[[:print:]]/).to_a.should == ["\u{1FAD}"]
|
||||
"\u{01C5}".match(/[[:print:]]/).to_a.should == ["\u{01C5}"]
|
||||
end
|
||||
|
||||
it "matches Unicode digits with [[:print:]]" do
|
||||
"\u{0660}".match(/[[:print:]]/).to_a.should == ["\u{0660}"]
|
||||
"\u{FF12}".match(/[[:print:]]/).to_a.should == ["\u{FF12}"]
|
||||
end
|
||||
|
||||
it "matches Unicode marks with [[:print:]]" do
|
||||
"\u{36F}".match(/[[:print:]]/).to_a.should == ["\u{36F}"]
|
||||
end
|
||||
|
||||
it "matches Unicode punctuation characters with [[:print:]]" do
|
||||
"\u{3F}".match(/[[:print:]]/).to_a.should == ["\u{3F}"]
|
||||
end
|
||||
|
||||
it "doesn't match Unicode control characters with [[:print:]]" do
|
||||
"\u{16}".match(/[[:print:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "match Unicode format characters with [[:print:]]" do
|
||||
"\u{2060}".match(/[[:print:]]/).to_a.should == ["\u{2060}"]
|
||||
end
|
||||
|
||||
it "match Unicode private-use characters with [[:print:]]" do
|
||||
"\u{E001}".match(/[[:print:]]/).to_a.should == ["\u{E001}"]
|
||||
end
|
||||
|
||||
|
||||
it "doesn't match Unicode lowercase letter characters with [[:punct:]]" do
|
||||
"\u{FF41}".match(/[[:punct:]]/).should be_nil
|
||||
"\u{1D484}".match(/[[:punct:]]/).should be_nil
|
||||
"\u{E8}".match(/[[:punct:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode uppercase letter characters with [[:punct:]]" do
|
||||
"\u{100}".match(/[[:punct:]]/).should be_nil
|
||||
"\u{130}".match(/[[:punct:]]/).should be_nil
|
||||
"\u{405}".match(/[[:punct:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode title-case characters with [[:punct:]]" do
|
||||
"\u{1F88}".match(/[[:punct:]]/).should be_nil
|
||||
"\u{1FAD}".match(/[[:punct:]]/).should be_nil
|
||||
"\u{01C5}".match(/[[:punct:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode digits with [[:punct:]]" do
|
||||
"\u{0660}".match(/[[:punct:]]/).should be_nil
|
||||
"\u{FF12}".match(/[[:punct:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode marks with [[:punct:]]" do
|
||||
"\u{36F}".match(/[[:punct:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "matches Unicode Pc characters with [[:punct:]]" do
|
||||
"\u{203F}".match(/[[:punct:]]/).to_a.should == ["\u{203F}"]
|
||||
end
|
||||
|
||||
it "matches Unicode Pd characters with [[:punct:]]" do
|
||||
"\u{2E17}".match(/[[:punct:]]/).to_a.should == ["\u{2E17}"]
|
||||
end
|
||||
|
||||
it "matches Unicode Ps characters with [[:punct:]]" do
|
||||
"\u{0F3A}".match(/[[:punct:]]/).to_a.should == ["\u{0F3A}"]
|
||||
end
|
||||
|
||||
it "matches Unicode Pe characters with [[:punct:]]" do
|
||||
"\u{2046}".match(/[[:punct:]]/).to_a.should == ["\u{2046}"]
|
||||
end
|
||||
|
||||
it "matches Unicode Pi characters with [[:punct:]]" do
|
||||
"\u{00AB}".match(/[[:punct:]]/).to_a.should == ["\u{00AB}"]
|
||||
end
|
||||
|
||||
it "matches Unicode Pf characters with [[:punct:]]" do
|
||||
"\u{201D}".match(/[[:punct:]]/).to_a.should == ["\u{201D}"]
|
||||
"\u{00BB}".match(/[[:punct:]]/).to_a.should == ["\u{00BB}"]
|
||||
end
|
||||
|
||||
it "matches Unicode Po characters with [[:punct:]]" do
|
||||
"\u{00BF}".match(/[[:punct:]]/).to_a.should == ["\u{00BF}"]
|
||||
end
|
||||
|
||||
it "doesn't match Unicode format characters with [[:punct:]]" do
|
||||
"\u{2060}".match(/[[:punct:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode private-use characters with [[:punct:]]" do
|
||||
"\u{E001}".match(/[[:punct:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode lowercase letter characters with [[:space:]]" do
|
||||
"\u{FF41}".match(/[[:space:]]/).should be_nil
|
||||
"\u{1D484}".match(/[[:space:]]/).should be_nil
|
||||
"\u{E8}".match(/[[:space:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode uppercase letter characters with [[:space:]]" do
|
||||
"\u{100}".match(/[[:space:]]/).should be_nil
|
||||
"\u{130}".match(/[[:space:]]/).should be_nil
|
||||
"\u{405}".match(/[[:space:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode title-case characters with [[:space:]]" do
|
||||
"\u{1F88}".match(/[[:space:]]/).should be_nil
|
||||
"\u{1FAD}".match(/[[:space:]]/).should be_nil
|
||||
"\u{01C5}".match(/[[:space:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode digits with [[:space:]]" do
|
||||
"\u{0660}".match(/[[:space:]]/).should be_nil
|
||||
"\u{FF12}".match(/[[:space:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode marks with [[:space:]]" do
|
||||
"\u{36F}".match(/[[:space:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "matches Unicode Zs characters with [[:space:]]" do
|
||||
"\u{205F}".match(/[[:space:]]/).to_a.should == ["\u{205F}"]
|
||||
end
|
||||
|
||||
it "matches Unicode Zl characters with [[:space:]]" do
|
||||
"\u{2028}".match(/[[:space:]]/).to_a.should == ["\u{2028}"]
|
||||
end
|
||||
|
||||
it "matches Unicode Zp characters with [[:space:]]" do
|
||||
"\u{2029}".match(/[[:space:]]/).to_a.should == ["\u{2029}"]
|
||||
end
|
||||
|
||||
it "doesn't match Unicode format characters with [[:space:]]" do
|
||||
"\u{2060}".match(/[[:space:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode private-use characters with [[:space:]]" do
|
||||
"\u{E001}".match(/[[:space:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode lowercase characters with [[:upper:]]" do
|
||||
"\u{FF41}".match(/[[:upper:]]/).should be_nil
|
||||
"\u{1D484}".match(/[[:upper:]]/).should be_nil
|
||||
"\u{E8}".match(/[[:upper:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "matches Unicode uppercase characters with [[:upper:]]" do
|
||||
"\u{100}".match(/[[:upper:]]/).to_a.should == ["\u{100}"]
|
||||
"\u{130}".match(/[[:upper:]]/).to_a.should == ["\u{130}"]
|
||||
"\u{405}".match(/[[:upper:]]/).to_a.should == ["\u{405}"]
|
||||
end
|
||||
|
||||
it "doesn't match Unicode title-case characters with [[:upper:]]" do
|
||||
"\u{1F88}".match(/[[:upper:]]/).should be_nil
|
||||
"\u{1FAD}".match(/[[:upper:]]/).should be_nil
|
||||
"\u{01C5}".match(/[[:upper:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode digits with [[:upper:]]" do
|
||||
"\u{0660}".match(/[[:upper:]]/).should be_nil
|
||||
"\u{FF12}".match(/[[:upper:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode marks with [[:upper:]]" do
|
||||
"\u{36F}".match(/[[:upper:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode punctuation characters with [[:upper:]]" do
|
||||
"\u{3F}".match(/[[:upper:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode control characters with [[:upper:]]" do
|
||||
"\u{16}".match(/[[:upper:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode format characters with [[:upper:]]" do
|
||||
"\u{2060}".match(/[[:upper:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode private-use characters with [[:upper:]]" do
|
||||
"\u{E001}".match(/[[:upper:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode letter characters [^a-fA-F] with [[:xdigit:]]" do
|
||||
"à".match(/[[:xdigit:]]/).should be_nil
|
||||
"g".match(/[[:xdigit:]]/).should be_nil
|
||||
"X".match(/[[:xdigit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "matches Unicode letter characters [a-fA-F] with [[:xdigit:]]" do
|
||||
"a".match(/[[:xdigit:]]/).to_a.should == ["a"]
|
||||
"F".match(/[[:xdigit:]]/).to_a.should == ["F"]
|
||||
end
|
||||
|
||||
it "doesn't match Unicode digits [^0-9] with [[:xdigit:]]" do
|
||||
"\u{0660}".match(/[[:xdigit:]]/).should be_nil
|
||||
"\u{FF12}".match(/[[:xdigit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode marks with [[:xdigit:]]" do
|
||||
"\u{36F}".match(/[[:xdigit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode punctuation characters with [[:xdigit:]]" do
|
||||
"\u{3F}".match(/[[:xdigit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode control characters with [[:xdigit:]]" do
|
||||
"\u{16}".match(/[[:xdigit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode format characters with [[:xdigit:]]" do
|
||||
"\u{2060}".match(/[[:xdigit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode private-use characters with [[:xdigit:]]" do
|
||||
"\u{E001}".match(/[[:xdigit:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "matches Unicode lowercase characters with [[:word:]]" do
|
||||
"\u{FF41}".match(/[[:word:]]/).to_a.should == ["\u{FF41}"]
|
||||
"\u{1D484}".match(/[[:word:]]/).to_a.should == ["\u{1D484}"]
|
||||
"\u{E8}".match(/[[:word:]]/).to_a.should == ["\u{E8}"]
|
||||
end
|
||||
|
||||
it "matches Unicode uppercase characters with [[:word:]]" do
|
||||
"\u{100}".match(/[[:word:]]/).to_a.should == ["\u{100}"]
|
||||
"\u{130}".match(/[[:word:]]/).to_a.should == ["\u{130}"]
|
||||
"\u{405}".match(/[[:word:]]/).to_a.should == ["\u{405}"]
|
||||
end
|
||||
|
||||
it "matches Unicode title-case characters with [[:word:]]" do
|
||||
"\u{1F88}".match(/[[:word:]]/).to_a.should == ["\u{1F88}"]
|
||||
"\u{1FAD}".match(/[[:word:]]/).to_a.should == ["\u{1FAD}"]
|
||||
"\u{01C5}".match(/[[:word:]]/).to_a.should == ["\u{01C5}"]
|
||||
end
|
||||
|
||||
it "matches Unicode decimal digits with [[:word:]]" do
|
||||
"\u{FF10}".match(/[[:word:]]/).to_a.should == ["\u{FF10}"]
|
||||
"\u{096C}".match(/[[:word:]]/).to_a.should == ["\u{096C}"]
|
||||
end
|
||||
|
||||
it "matches Unicode marks with [[:word:]]" do
|
||||
"\u{36F}".match(/[[:word:]]/).to_a.should == ["\u{36F}"]
|
||||
end
|
||||
|
||||
it "match Unicode Nl characters with [[:word:]]" do
|
||||
"\u{16EE}".match(/[[:word:]]/).to_a.should == ["\u{16EE}"]
|
||||
end
|
||||
|
||||
it "doesn't match Unicode No characters with [[:word:]]" do
|
||||
"\u{17F0}".match(/[[:word:]]/).should be_nil
|
||||
end
|
||||
it "doesn't match Unicode punctuation characters with [[:word:]]" do
|
||||
"\u{3F}".match(/[[:word:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode control characters with [[:word:]]" do
|
||||
"\u{16}".match(/[[:word:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode format characters with [[:word:]]" do
|
||||
"\u{2060}".match(/[[:word:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "doesn't match Unicode private-use characters with [[:word:]]" do
|
||||
"\u{E001}".match(/[[:word:]]/).should be_nil
|
||||
end
|
||||
|
||||
it "matches unicode named character properties" do
|
||||
"a1".match(/\p{Alpha}/).to_a.should == ["a"]
|
||||
end
|
||||
|
||||
it "matches unicode abbreviated character properties" do
|
||||
"a1".match(/\p{L}/).to_a.should == ["a"]
|
||||
end
|
||||
|
||||
it "matches unicode script properties" do
|
||||
"a\u06E9b".match(/\p{Arabic}/).to_a.should == ["\u06E9"]
|
||||
end
|
||||
|
||||
it "matches unicode Han properties" do
|
||||
"松本行弘 Ruby".match(/\p{Han}+/u).to_a.should == ["松本行弘"]
|
||||
end
|
||||
|
||||
it "matches unicode Hiragana properties" do
|
||||
"Ruby(ルビー)、まつもとゆきひろ".match(/\p{Hiragana}+/u).to_a.should == ["まつもとゆきひろ"]
|
||||
end
|
||||
|
||||
it "matches unicode Katakana properties" do
|
||||
"Ruby(ルビー)、まつもとゆきひろ".match(/\p{Katakana}+/u).to_a.should == ["ルビ"]
|
||||
end
|
||||
|
||||
it "matches unicode Hangul properties" do
|
||||
"루비(Ruby)".match(/\p{Hangul}+/u).to_a.should == ["루비"]
|
||||
end
|
||||
end
|
103
spec/ruby/language/regexp/encoding_spec.rb
Normal file
103
spec/ruby/language/regexp/encoding_spec.rb
Normal file
|
@ -0,0 +1,103 @@
|
|||
# -*- encoding: binary -*-
|
||||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Regexps with encoding modifiers" do
|
||||
it "supports /e (EUC encoding)" do
|
||||
match = /./e.match("\303\251".force_encoding(Encoding::EUC_JP))
|
||||
match.to_a.should == ["\303\251".force_encoding(Encoding::EUC_JP)]
|
||||
end
|
||||
|
||||
it "supports /e (EUC encoding) with interpolation" do
|
||||
match = /#{/./}/e.match("\303\251".force_encoding(Encoding::EUC_JP))
|
||||
match.to_a.should == ["\303\251".force_encoding(Encoding::EUC_JP)]
|
||||
end
|
||||
|
||||
it "supports /e (EUC encoding) with interpolation /o" do
|
||||
match = /#{/./}/e.match("\303\251".force_encoding(Encoding::EUC_JP))
|
||||
match.to_a.should == ["\303\251".force_encoding(Encoding::EUC_JP)]
|
||||
end
|
||||
|
||||
it 'uses EUC-JP as /e encoding' do
|
||||
/./e.encoding.should == Encoding::EUC_JP
|
||||
end
|
||||
|
||||
it 'preserves EUC-JP as /e encoding through interpolation' do
|
||||
/#{/./}/e.encoding.should == Encoding::EUC_JP
|
||||
end
|
||||
|
||||
it "supports /n (No encoding)" do
|
||||
/./n.match("\303\251").to_a.should == ["\303"]
|
||||
end
|
||||
|
||||
it "supports /n (No encoding) with interpolation" do
|
||||
/#{/./}/n.match("\303\251").to_a.should == ["\303"]
|
||||
end
|
||||
|
||||
it "supports /n (No encoding) with interpolation /o" do
|
||||
/#{/./}/n.match("\303\251").to_a.should == ["\303"]
|
||||
end
|
||||
|
||||
it 'uses US-ASCII as /n encoding if all chars are 7-bit' do
|
||||
/./n.encoding.should == Encoding::US_ASCII
|
||||
end
|
||||
|
||||
it 'uses ASCII-8BIT as /n encoding if not all chars are 7-bit' do
|
||||
/\xFF/n.encoding.should == Encoding::ASCII_8BIT
|
||||
end
|
||||
|
||||
it 'preserves US-ASCII as /n encoding through interpolation if all chars are 7-bit' do
|
||||
/.#{/./}/n.encoding.should == Encoding::US_ASCII
|
||||
end
|
||||
|
||||
it 'preserves ASCII-8BIT as /n encoding through interpolation if all chars are 7-bit' do
|
||||
/\xFF#{/./}/n.encoding.should == Encoding::ASCII_8BIT
|
||||
end
|
||||
|
||||
it "supports /s (Windows_31J encoding)" do
|
||||
match = /./s.match("\303\251".force_encoding(Encoding::Windows_31J))
|
||||
match.to_a.should == ["\303".force_encoding(Encoding::Windows_31J)]
|
||||
end
|
||||
|
||||
it "supports /s (Windows_31J encoding) with interpolation" do
|
||||
match = /#{/./}/s.match("\303\251".force_encoding(Encoding::Windows_31J))
|
||||
match.to_a.should == ["\303".force_encoding(Encoding::Windows_31J)]
|
||||
end
|
||||
|
||||
it "supports /s (Windows_31J encoding) with interpolation and /o" do
|
||||
match = /#{/./}/s.match("\303\251".force_encoding(Encoding::Windows_31J))
|
||||
match.to_a.should == ["\303".force_encoding(Encoding::Windows_31J)]
|
||||
end
|
||||
|
||||
it 'uses Windows-31J as /s encoding' do
|
||||
/./s.encoding.should == Encoding::Windows_31J
|
||||
end
|
||||
|
||||
it 'preserves Windows-31J as /s encoding through interpolation' do
|
||||
/#{/./}/s.encoding.should == Encoding::Windows_31J
|
||||
end
|
||||
|
||||
it "supports /u (UTF8 encoding)" do
|
||||
/./u.match("\303\251".force_encoding('utf-8')).to_a.should == ["\u{e9}"]
|
||||
end
|
||||
|
||||
it "supports /u (UTF8 encoding) with interpolation" do
|
||||
/#{/./}/u.match("\303\251".force_encoding('utf-8')).to_a.should == ["\u{e9}"]
|
||||
end
|
||||
|
||||
it "supports /u (UTF8 encoding) with interpolation and /o" do
|
||||
/#{/./}/u.match("\303\251".force_encoding('utf-8')).to_a.should == ["\u{e9}"]
|
||||
end
|
||||
|
||||
it 'uses UTF-8 as /u encoding' do
|
||||
/./u.encoding.should == Encoding::UTF_8
|
||||
end
|
||||
|
||||
it 'preserves UTF-8 as /u encoding through interpolation' do
|
||||
/#{/./}/u.encoding.should == Encoding::UTF_8
|
||||
end
|
||||
|
||||
it "selects last of multiple encoding specifiers" do
|
||||
/foo/ensuensuens.should == /foo/s
|
||||
end
|
||||
end
|
81
spec/ruby/language/regexp/escapes_spec.rb
Normal file
81
spec/ruby/language/regexp/escapes_spec.rb
Normal file
|
@ -0,0 +1,81 @@
|
|||
# -*- encoding: binary -*-
|
||||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Regexps with escape characters" do
|
||||
it "they're supported" do
|
||||
/\t/.match("\t").to_a.should == ["\t"] # horizontal tab
|
||||
/\v/.match("\v").to_a.should == ["\v"] # vertical tab
|
||||
/\n/.match("\n").to_a.should == ["\n"] # newline
|
||||
/\r/.match("\r").to_a.should == ["\r"] # return
|
||||
/\f/.match("\f").to_a.should == ["\f"] # form feed
|
||||
/\a/.match("\a").to_a.should == ["\a"] # bell
|
||||
/\e/.match("\e").to_a.should == ["\e"] # escape
|
||||
|
||||
# \nnn octal char (encoded byte value)
|
||||
end
|
||||
|
||||
it "support quoting meta-characters via escape sequence" do
|
||||
/\\/.match("\\").to_a.should == ["\\"]
|
||||
/\//.match("/").to_a.should == ["/"]
|
||||
# parenthesis, etc
|
||||
/\(/.match("(").to_a.should == ["("]
|
||||
/\)/.match(")").to_a.should == [")"]
|
||||
/\[/.match("[").to_a.should == ["["]
|
||||
/\]/.match("]").to_a.should == ["]"]
|
||||
/\{/.match("{").to_a.should == ["{"]
|
||||
/\}/.match("}").to_a.should == ["}"]
|
||||
# alternation separator
|
||||
/\|/.match("|").to_a.should == ["|"]
|
||||
# quantifiers
|
||||
/\?/.match("?").to_a.should == ["?"]
|
||||
/\./.match(".").to_a.should == ["."]
|
||||
/\*/.match("*").to_a.should == ["*"]
|
||||
/\+/.match("+").to_a.should == ["+"]
|
||||
# line anchors
|
||||
/\^/.match("^").to_a.should == ["^"]
|
||||
/\$/.match("$").to_a.should == ["$"]
|
||||
end
|
||||
|
||||
it "allows any character to be escaped" do
|
||||
/\y/.match("y").to_a.should == ["y"]
|
||||
end
|
||||
|
||||
it "support \\x (hex characters)" do
|
||||
/\xA/.match("\nxyz").to_a.should == ["\n"]
|
||||
/\x0A/.match("\n").to_a.should == ["\n"]
|
||||
/\xAA/.match("\nA").should be_nil
|
||||
/\x0AA/.match("\nA").to_a.should == ["\nA"]
|
||||
/\xAG/.match("\nG").to_a.should == ["\nG"]
|
||||
# Non-matches
|
||||
lambda { eval('/\xG/') }.should raise_error(SyntaxError)
|
||||
|
||||
# \x{7HHHHHHH} wide hexadecimal char (character code point value)
|
||||
end
|
||||
|
||||
it "support \\c (control characters)" do
|
||||
#/\c \c@\c`/.match("\00\00\00").to_a.should == ["\00\00\00"]
|
||||
/\c#\cc\cC/.match("\03\03\03").to_a.should == ["\03\03\03"]
|
||||
/\c'\cG\cg/.match("\a\a\a").to_a.should == ["\a\a\a"]
|
||||
/\c(\cH\ch/.match("\b\b\b").to_a.should == ["\b\b\b"]
|
||||
/\c)\cI\ci/.match("\t\t\t").to_a.should == ["\t\t\t"]
|
||||
/\c*\cJ\cj/.match("\n\n\n").to_a.should == ["\n\n\n"]
|
||||
/\c+\cK\ck/.match("\v\v\v").to_a.should == ["\v\v\v"]
|
||||
/\c,\cL\cl/.match("\f\f\f").to_a.should == ["\f\f\f"]
|
||||
/\c-\cM\cm/.match("\r\r\r").to_a.should == ["\r\r\r"]
|
||||
|
||||
/\cJ/.match("\r").should be_nil
|
||||
|
||||
# Parsing precedence
|
||||
/\cJ+/.match("\n\n").to_a.should == ["\n\n"] # Quantifers apply to entire escape sequence
|
||||
/\\cJ/.match("\\cJ").to_a.should == ["\\cJ"]
|
||||
lambda { eval('/[abc\x]/') }.should raise_error(SyntaxError) # \x is treated as a escape sequence even inside a character class
|
||||
# Syntax error
|
||||
lambda { eval('/\c/') }.should raise_error(SyntaxError)
|
||||
|
||||
# \cx control char (character code point value)
|
||||
# \C-x control char (character code point value)
|
||||
# \M-x meta (x|0x80) (character code point value)
|
||||
# \M-\C-x meta control char (character code point value)
|
||||
end
|
||||
end
|
23
spec/ruby/language/regexp/grouping_spec.rb
Normal file
23
spec/ruby/language/regexp/grouping_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Regexps with grouping" do
|
||||
it "support ()" do
|
||||
/(a)/.match("a").to_a.should == ["a", "a"]
|
||||
end
|
||||
|
||||
it "allows groups to be nested" do
|
||||
md = /(hay(st)a)ck/.match('haystack')
|
||||
md.to_a.should == ['haystack','haysta', 'st']
|
||||
end
|
||||
|
||||
it "raises a SyntaxError when parentheses aren't balanced" do
|
||||
lambda { eval "/(hay(st)ack/" }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "supports (?: ) (non-capturing group)" do
|
||||
/(?:foo)(bar)/.match("foobar").to_a.should == ["foobar", "bar"]
|
||||
# Parsing precedence
|
||||
/(?:xdigit:)/.match("xdigit:").to_a.should == ["xdigit:"]
|
||||
end
|
||||
end
|
58
spec/ruby/language/regexp/interpolation_spec.rb
Normal file
58
spec/ruby/language/regexp/interpolation_spec.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Regexps with interpolation" do
|
||||
|
||||
it "allows interpolation of strings" do
|
||||
str = "foo|bar"
|
||||
/#{str}/.should == /foo|bar/
|
||||
end
|
||||
|
||||
it "allows interpolation of literal regexps" do
|
||||
re = /foo|bar/
|
||||
/#{re}/.should == /(?-mix:foo|bar)/
|
||||
end
|
||||
|
||||
it "allows interpolation of any object that responds to to_s" do
|
||||
o = Object.new
|
||||
def o.to_s
|
||||
"object_with_to_s"
|
||||
end
|
||||
/#{o}/.should == /object_with_to_s/
|
||||
end
|
||||
|
||||
it "allows interpolation which mixes modifiers" do
|
||||
re = /foo/i
|
||||
/#{re} bar/m.should == /(?i-mx:foo) bar/m
|
||||
end
|
||||
|
||||
it "allows interpolation to interact with other Regexp constructs" do
|
||||
str = "foo)|(bar"
|
||||
/(#{str})/.should == /(foo)|(bar)/
|
||||
|
||||
str = "a"
|
||||
/[#{str}-z]/.should == /[a-z]/
|
||||
end
|
||||
|
||||
it "gives precedence to escape sequences over substitution" do
|
||||
str = "J"
|
||||
/\c#{str}/.to_s.should == '(?-mix:\c#' + '{str})'
|
||||
end
|
||||
|
||||
it "throws RegexpError for malformed interpolation" do
|
||||
s = ""
|
||||
lambda { /(#{s}/ }.should raise_error(RegexpError)
|
||||
s = "("
|
||||
lambda { /#{s}/ }.should raise_error(RegexpError)
|
||||
end
|
||||
|
||||
it "allows interpolation in extended mode" do
|
||||
var = "#comment\n foo #comment\n | bar"
|
||||
(/#{var}/x =~ "foo").should == (/foo|bar/ =~ "foo")
|
||||
end
|
||||
|
||||
it "allows escape sequences in interpolated regexps" do
|
||||
escape_seq = %r{"\x80"}n
|
||||
%r{#{escape_seq}}n.should == /(?-mix:"\x80")/n
|
||||
end
|
||||
end
|
110
spec/ruby/language/regexp/modifiers_spec.rb
Normal file
110
spec/ruby/language/regexp/modifiers_spec.rb
Normal file
|
@ -0,0 +1,110 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Regexps with modifers" do
|
||||
it "supports /i (case-insensitive)" do
|
||||
/foo/i.match("FOO").to_a.should == ["FOO"]
|
||||
end
|
||||
|
||||
it "supports /m (multiline)" do
|
||||
/foo.bar/m.match("foo\nbar").to_a.should == ["foo\nbar"]
|
||||
/foo.bar/.match("foo\nbar").should be_nil
|
||||
end
|
||||
|
||||
it "supports /x (extended syntax)" do
|
||||
/\d +/x.match("abc123").to_a.should == ["123"] # Quantifiers can be separated from the expression they apply to
|
||||
end
|
||||
|
||||
it "supports /o (once)" do
|
||||
2.times do |i|
|
||||
/#{i}/o.should == /0/
|
||||
end
|
||||
end
|
||||
|
||||
it "invokes substitutions for /o only once" do
|
||||
ScratchPad.record []
|
||||
o = Object.new
|
||||
def o.to_s
|
||||
ScratchPad << :to_s
|
||||
"class_with_to_s"
|
||||
end
|
||||
eval "2.times { /#{o}/o }"
|
||||
ScratchPad.recorded.should == [:to_s]
|
||||
end
|
||||
|
||||
it "supports modifier combinations" do
|
||||
/foo/imox.match("foo").to_a.should == ["foo"]
|
||||
/foo/imoximox.match("foo").to_a.should == ["foo"]
|
||||
|
||||
lambda { eval('/foo/a') }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "supports (?imx-imx) (inline modifiers)" do
|
||||
/(?i)foo/.match("FOO").to_a.should == ["FOO"]
|
||||
/foo(?i)/.match("FOO").should be_nil
|
||||
# Interaction with /i
|
||||
/(?-i)foo/i.match("FOO").should be_nil
|
||||
/foo(?-i)/i.match("FOO").to_a.should == ["FOO"]
|
||||
# Multiple uses
|
||||
/foo (?i)bar (?-i)baz/.match("foo BAR baz").to_a.should == ["foo BAR baz"]
|
||||
/foo (?i)bar (?-i)baz/.match("foo BAR BAZ").should be_nil
|
||||
|
||||
/(?m)./.match("\n").to_a.should == ["\n"]
|
||||
/.(?m)/.match("\n").should be_nil
|
||||
# Interaction with /m
|
||||
/(?-m)./m.match("\n").should be_nil
|
||||
/.(?-m)/m.match("\n").to_a.should == ["\n"]
|
||||
# Multiple uses
|
||||
/. (?m). (?-m)./.match(". \n .").to_a.should == [". \n ."]
|
||||
/. (?m). (?-m)./.match(". \n \n").should be_nil
|
||||
|
||||
/(?x) foo /.match("foo").to_a.should == ["foo"]
|
||||
/ foo (?x)/.match("foo").should be_nil
|
||||
# Interaction with /x
|
||||
/(?-x) foo /x.match("foo").should be_nil
|
||||
/ foo (?-x)/x.match("foo").to_a.should == ["foo"]
|
||||
# Multiple uses
|
||||
/( foo )(?x)( bar )(?-x)( baz )/.match(" foo bar baz ").to_a.should == [" foo bar baz ", " foo ", "bar", " baz "]
|
||||
/( foo )(?x)( bar )(?-x)( baz )/.match(" foo barbaz").should be_nil
|
||||
|
||||
# Parsing
|
||||
/(?i-i)foo/.match("FOO").should be_nil
|
||||
/(?ii)foo/.match("FOO").to_a.should == ["FOO"]
|
||||
/(?-)foo/.match("foo").to_a.should == ["foo"]
|
||||
lambda { eval('/(?o)/') }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "supports (?imx-imx:expr) (scoped inline modifiers)" do
|
||||
/foo (?i:bar) baz/.match("foo BAR baz").to_a.should == ["foo BAR baz"]
|
||||
/foo (?i:bar) baz/.match("foo BAR BAZ").should be_nil
|
||||
/foo (?-i:bar) baz/i.match("foo BAR BAZ").should be_nil
|
||||
|
||||
/. (?m:.) ./.match(". \n .").to_a.should == [". \n ."]
|
||||
/. (?m:.) ./.match(". \n \n").should be_nil
|
||||
/. (?-m:.) ./m.match("\n \n \n").should be_nil
|
||||
|
||||
/( foo )(?x: bar )( baz )/.match(" foo bar baz ").to_a.should == [" foo bar baz ", " foo ", " baz "]
|
||||
/( foo )(?x: bar )( baz )/.match(" foo barbaz").should be_nil
|
||||
/( foo )(?-x: bar )( baz )/x.match("foo bar baz").to_a.should == ["foo bar baz", "foo", "baz"]
|
||||
|
||||
# Parsing
|
||||
/(?i-i:foo)/.match("FOO").should be_nil
|
||||
/(?ii:foo)/.match("FOO").to_a.should == ["FOO"]
|
||||
/(?-:)foo/.match("foo").to_a.should == ["foo"]
|
||||
lambda { eval('/(?o:)/') }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "supports . with /m" do
|
||||
# Basic matching
|
||||
/./m.match("\n").to_a.should == ["\n"]
|
||||
end
|
||||
|
||||
it "supports ASII/Unicode modifiers" do
|
||||
eval('/(?a)[[:alpha:]]+/').match("a\u3042").to_a.should == ["a"]
|
||||
eval('/(?d)[[:alpha:]]+/').match("a\u3042").to_a.should == ["a\u3042"]
|
||||
eval('/(?u)[[:alpha:]]+/').match("a\u3042").to_a.should == ["a\u3042"]
|
||||
eval('/(?a)\w+/').match("a\u3042").to_a.should == ["a"]
|
||||
eval('/(?d)\w+/').match("a\u3042").to_a.should == ["a"]
|
||||
eval('/(?u)\w+/').match("a\u3042").to_a.should == ["a\u3042"]
|
||||
end
|
||||
end
|
57
spec/ruby/language/regexp/repetition_spec.rb
Normal file
57
spec/ruby/language/regexp/repetition_spec.rb
Normal file
|
@ -0,0 +1,57 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Regexps with repetition" do
|
||||
it "supports * (0 or more of previous subexpression)" do
|
||||
/a*/.match("aaa").to_a.should == ["aaa"]
|
||||
/a*/.match("bbb").to_a.should == [""]
|
||||
/<.*>/.match("<a>foo</a>").to_a.should == ["<a>foo</a>"] # it is greedy
|
||||
end
|
||||
|
||||
it "supports *? (0 or more of previous subexpression - lazy)" do
|
||||
/a*?/.match("aaa").to_a.should == [""]
|
||||
/<.*?>/.match("<a>foo</a>").to_a.should == ["<a>"]
|
||||
end
|
||||
|
||||
it "supports + (1 or more of previous subexpression)" do
|
||||
/a+/.match("aaa").to_a.should == ["aaa"]
|
||||
/a+/.match("bbb").should be_nil
|
||||
/<.+>/.match("<a>foo</a>").to_a.should == ["<a>foo</a>"] # it is greedy
|
||||
end
|
||||
|
||||
it "supports +? (0 or more of previous subexpression - lazy)" do
|
||||
/a+?/.match("aaa").to_a.should == ["a"]
|
||||
/<.+?>/.match("<a>foo</a>").to_a.should == ["<a>"]
|
||||
end
|
||||
|
||||
it "supports {m,n} (m to n of previous subexpression)" do
|
||||
/a{2,4}/.match("aaaaaa").to_a.should == ["aaaa"]
|
||||
/<.{1,}>/.match("<a>foo</a>").to_a.should == ["<a>foo</a>"] # it is greedy
|
||||
end
|
||||
|
||||
it "supports {m,n}? (m to n of previous subexpression) - lazy)" do
|
||||
/<.{1,}?>/.match("<a>foo</a>").to_a.should == ["<a>"]
|
||||
/.([0-9]){3,5}?foo/.match("9876543210foo").to_a.should == ["543210foo", "0"]
|
||||
end
|
||||
|
||||
ruby_version_is ""..."2.4" do
|
||||
it "does not treat {m,n}+ as possessive" do
|
||||
@regexp = eval "/foo(A{0,1}+)Abar/"
|
||||
@regexp.match("fooAAAbar").to_a.should == ["fooAAAbar", "AA"]
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "2.4" do
|
||||
it "does not treat {m,n}+ as possessive" do
|
||||
-> {
|
||||
@regexp = eval "/foo(A{0,1}+)Abar/"
|
||||
}.should complain(/nested repeat operato/)
|
||||
@regexp.match("fooAAAbar").to_a.should == ["fooAAAbar", "AA"]
|
||||
end
|
||||
end
|
||||
|
||||
it "supports ? (0 or 1 of previous subexpression)" do
|
||||
/a?/.match("aaa").to_a.should == ["a"]
|
||||
/a?/.match("bbb").to_a.should == [""]
|
||||
end
|
||||
end
|
150
spec/ruby/language/regexp_spec.rb
Normal file
150
spec/ruby/language/regexp_spec.rb
Normal file
|
@ -0,0 +1,150 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Literal Regexps" do
|
||||
it "matches against $_ (last input) in a conditional if no explicit matchee provided" do
|
||||
-> {
|
||||
eval <<-EOR
|
||||
$_ = nil
|
||||
(true if /foo/).should_not == true
|
||||
|
||||
$_ = "foo"
|
||||
(true if /foo/).should == true
|
||||
EOR
|
||||
}.should complain(/regex literal in condition/)
|
||||
end
|
||||
|
||||
it "yields a Regexp" do
|
||||
/Hello/.should be_kind_of(Regexp)
|
||||
end
|
||||
|
||||
it "caches the Regexp object" do
|
||||
rs = []
|
||||
2.times do |i|
|
||||
rs << /foo/
|
||||
end
|
||||
rs[0].should equal(rs[1])
|
||||
end
|
||||
|
||||
it "throws SyntaxError for malformed literals" do
|
||||
lambda { eval('/(/') }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
#############################################################################
|
||||
# %r
|
||||
#############################################################################
|
||||
|
||||
it "supports paired delimiters with %r" do
|
||||
LanguageSpecs.paired_delimiters.each do |p0, p1|
|
||||
eval("%r#{p0} foo #{p1}").should == / foo /
|
||||
end
|
||||
end
|
||||
|
||||
it "supports grouping constructs that are also paired delimiters" do
|
||||
LanguageSpecs.paired_delimiters.each do |p0, p1|
|
||||
eval("%r#{p0} () [c]{1} #{p1}").should == / () [c]{1} /
|
||||
end
|
||||
end
|
||||
|
||||
it "allows second part of paired delimiters to be used as non-paired delimiters" do
|
||||
LanguageSpecs.paired_delimiters.each do |p0, p1|
|
||||
eval("%r#{p1} foo #{p1}").should == / foo /
|
||||
end
|
||||
end
|
||||
|
||||
it "disallows first part of paired delimiters to be used as non-paired delimiters" do
|
||||
LanguageSpecs.paired_delimiters.each do |p0, p1|
|
||||
lambda { eval("%r#{p0} foo #{p0}") }.should raise_error(SyntaxError)
|
||||
end
|
||||
end
|
||||
|
||||
it "supports non-paired delimiters delimiters with %r" do
|
||||
LanguageSpecs.non_paired_delimiters.each do |c|
|
||||
eval("%r#{c} foo #{c}").should == / foo /
|
||||
end
|
||||
end
|
||||
|
||||
it "disallows alphabets as non-paired delimiter with %r" do
|
||||
lambda { eval('%ra foo a') }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "disallows spaces after %r and delimiter" do
|
||||
lambda { eval('%r !foo!') }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "allows unescaped / to be used with %r" do
|
||||
%r[/].to_s.should == /\//.to_s
|
||||
end
|
||||
|
||||
|
||||
#############################################################################
|
||||
# Specs for the matching semantics
|
||||
#############################################################################
|
||||
|
||||
it "supports . (any character except line terminator)" do
|
||||
# Basic matching
|
||||
/./.match("foo").to_a.should == ["f"]
|
||||
# Basic non-matching
|
||||
/./.match("").should be_nil
|
||||
/./.match("\n").should be_nil
|
||||
/./.match("\0").to_a.should == ["\0"]
|
||||
end
|
||||
|
||||
|
||||
it "supports | (alternations)" do
|
||||
/a|b/.match("a").to_a.should == ["a"]
|
||||
end
|
||||
|
||||
it "supports (?> ) (embedded subexpression)" do
|
||||
/(?>foo)(?>bar)/.match("foobar").to_a.should == ["foobar"]
|
||||
/(?>foo*)obar/.match("foooooooobar").should be_nil # it is possesive
|
||||
end
|
||||
|
||||
it "supports (?# )" do
|
||||
/foo(?#comment)bar/.match("foobar").to_a.should == ["foobar"]
|
||||
/foo(?#)bar/.match("foobar").to_a.should == ["foobar"]
|
||||
end
|
||||
|
||||
it "supports (?<= ) (positive lookbehind)" do
|
||||
/foo.(?<=\d)/.match("fooA foo1").to_a.should == ["foo1"]
|
||||
end
|
||||
|
||||
it "supports (?<! ) (negative lookbehind)" do
|
||||
/foo.(?<!\d)/.match("foo1 fooA").to_a.should == ["fooA"]
|
||||
end
|
||||
|
||||
it "supports \\g (named backreference)" do
|
||||
/(?<foo>foo.)bar\g<foo>/.match("foo1barfoo2").to_a.should == ["foo1barfoo2", "foo2"]
|
||||
end
|
||||
|
||||
it "supports character class composition" do
|
||||
/[a-z&&[^a-c]]+/.match("abcdef").to_a.should == ["def"]
|
||||
/[a-z&&[^d-i&&[^d-f]]]+/.match("abcdefghi").to_a.should == ["abcdef"]
|
||||
end
|
||||
|
||||
it "supports possessive quantifiers" do
|
||||
/fooA++bar/.match("fooAAAbar").to_a.should == ["fooAAAbar"]
|
||||
|
||||
/fooA++Abar/.match("fooAAAbar").should be_nil
|
||||
/fooA?+Abar/.match("fooAAAbar").should be_nil
|
||||
/fooA*+Abar/.match("fooAAAbar").should be_nil
|
||||
end
|
||||
|
||||
it "supports conditional regular expressions with positional capture groups" do
|
||||
pattern = /\A(foo)?(?(1)(T)|(F))\z/
|
||||
|
||||
pattern.should =~ 'fooT'
|
||||
pattern.should =~ 'F'
|
||||
pattern.should_not =~ 'fooF'
|
||||
pattern.should_not =~ 'T'
|
||||
end
|
||||
|
||||
it "supports conditional regular expressions with named capture groups" do
|
||||
pattern = /\A(?<word>foo)?(?(<word>)(T)|(F))\z/
|
||||
|
||||
pattern.should =~ 'fooT'
|
||||
pattern.should =~ 'F'
|
||||
pattern.should_not =~ 'fooF'
|
||||
pattern.should_not =~ 'T'
|
||||
end
|
||||
end
|
293
spec/ruby/language/rescue_spec.rb
Normal file
293
spec/ruby/language/rescue_spec.rb
Normal file
|
@ -0,0 +1,293 @@
|
|||
require File.expand_path('../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/rescue', __FILE__)
|
||||
|
||||
class SpecificExampleException < StandardError
|
||||
end
|
||||
class OtherCustomException < StandardError
|
||||
end
|
||||
class ArbitraryException < StandardError
|
||||
end
|
||||
|
||||
exception_list = [SpecificExampleException, ArbitraryException]
|
||||
|
||||
describe "The rescue keyword" do
|
||||
before :each do
|
||||
ScratchPad.record []
|
||||
end
|
||||
|
||||
it "can be used to handle a specific exception" do
|
||||
begin
|
||||
raise SpecificExampleException, "Raising this to be handled below"
|
||||
rescue SpecificExampleException
|
||||
:caught
|
||||
end.should == :caught
|
||||
end
|
||||
|
||||
it "can capture the raised exception in a local variable" do
|
||||
begin
|
||||
raise SpecificExampleException, "some text"
|
||||
rescue SpecificExampleException => e
|
||||
e.message.should == "some text"
|
||||
end
|
||||
end
|
||||
|
||||
it "can rescue multiple raised exceptions with a single rescue block" do
|
||||
[lambda{raise ArbitraryException}, lambda{raise SpecificExampleException}].map do |block|
|
||||
begin
|
||||
block.call
|
||||
rescue SpecificExampleException, ArbitraryException
|
||||
:caught
|
||||
end
|
||||
end.should == [:caught, :caught]
|
||||
end
|
||||
|
||||
it "can rescue a splatted list of exceptions" do
|
||||
caught_it = false
|
||||
begin
|
||||
raise SpecificExampleException, "not important"
|
||||
rescue *exception_list
|
||||
caught_it = true
|
||||
end
|
||||
caught_it.should be_true
|
||||
caught = []
|
||||
[lambda{raise ArbitraryException}, lambda{raise SpecificExampleException}].each do |block|
|
||||
begin
|
||||
block.call
|
||||
rescue *exception_list
|
||||
caught << $!
|
||||
end
|
||||
end
|
||||
caught.size.should == 2
|
||||
exception_list.each do |exception_class|
|
||||
caught.map{|e| e.class}.should include(exception_class)
|
||||
end
|
||||
end
|
||||
|
||||
it "can combine a splatted list of exceptions with a literal list of exceptions" do
|
||||
caught_it = false
|
||||
begin
|
||||
raise SpecificExampleException, "not important"
|
||||
rescue ArbitraryException, *exception_list
|
||||
caught_it = true
|
||||
end
|
||||
caught_it.should be_true
|
||||
caught = []
|
||||
[lambda{raise ArbitraryException}, lambda{raise SpecificExampleException}].each do |block|
|
||||
begin
|
||||
block.call
|
||||
rescue ArbitraryException, *exception_list
|
||||
caught << $!
|
||||
end
|
||||
end
|
||||
caught.size.should == 2
|
||||
exception_list.each do |exception_class|
|
||||
caught.map{|e| e.class}.should include(exception_class)
|
||||
end
|
||||
end
|
||||
|
||||
it "will only rescue the specified exceptions when doing a splat rescue" do
|
||||
lambda do
|
||||
begin
|
||||
raise OtherCustomException, "not rescued!"
|
||||
rescue *exception_list
|
||||
end
|
||||
end.should raise_error(OtherCustomException)
|
||||
end
|
||||
|
||||
it "will execute an else block only if no exceptions were raised" do
|
||||
result = begin
|
||||
ScratchPad << :one
|
||||
rescue
|
||||
ScratchPad << :does_not_run
|
||||
else
|
||||
ScratchPad << :two
|
||||
:val
|
||||
end
|
||||
result.should == :val
|
||||
ScratchPad.recorded.should == [:one, :two]
|
||||
end
|
||||
|
||||
it "will execute an else block with ensure only if no exceptions were raised" do
|
||||
result = begin
|
||||
ScratchPad << :one
|
||||
rescue
|
||||
ScratchPad << :does_not_run
|
||||
else
|
||||
ScratchPad << :two
|
||||
:val
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
:ensure_val
|
||||
end
|
||||
result.should == :val
|
||||
ScratchPad.recorded.should == [:one, :two, :ensure]
|
||||
end
|
||||
|
||||
it "will execute an else block only if no exceptions were raised in a method" do
|
||||
result = RescueSpecs.begin_else(false)
|
||||
result.should == :val
|
||||
ScratchPad.recorded.should == [:one, :else_ran]
|
||||
end
|
||||
|
||||
it "will execute an else block with ensure only if no exceptions were raised in a method" do
|
||||
result = RescueSpecs.begin_else_ensure(false)
|
||||
result.should == :val
|
||||
ScratchPad.recorded.should == [:one, :else_ran, :ensure_ran]
|
||||
end
|
||||
|
||||
it "will execute an else block but use the outer scope return value in a method" do
|
||||
result = RescueSpecs.begin_else_return(false)
|
||||
result.should == :return_val
|
||||
ScratchPad.recorded.should == [:one, :else_ran, :outside_begin]
|
||||
end
|
||||
|
||||
it "will execute an else block with ensure but use the outer scope return value in a method" do
|
||||
result = RescueSpecs.begin_else_return_ensure(false)
|
||||
result.should == :return_val
|
||||
ScratchPad.recorded.should == [:one, :else_ran, :ensure_ran, :outside_begin]
|
||||
end
|
||||
|
||||
it "will not execute an else block if an exception was raised" do
|
||||
result = begin
|
||||
ScratchPad << :one
|
||||
raise "an error occurred"
|
||||
rescue
|
||||
ScratchPad << :two
|
||||
:val
|
||||
else
|
||||
ScratchPad << :does_not_run
|
||||
end
|
||||
result.should == :val
|
||||
ScratchPad.recorded.should == [:one, :two]
|
||||
end
|
||||
|
||||
it "will not execute an else block with ensure if an exception was raised" do
|
||||
result = begin
|
||||
ScratchPad << :one
|
||||
raise "an error occurred"
|
||||
rescue
|
||||
ScratchPad << :two
|
||||
:val
|
||||
else
|
||||
ScratchPad << :does_not_run
|
||||
ensure
|
||||
ScratchPad << :ensure
|
||||
:ensure_val
|
||||
end
|
||||
result.should == :val
|
||||
ScratchPad.recorded.should == [:one, :two, :ensure]
|
||||
end
|
||||
|
||||
it "will not execute an else block if an exception was raised in a method" do
|
||||
result = RescueSpecs.begin_else(true)
|
||||
result.should == :rescue_val
|
||||
ScratchPad.recorded.should == [:one, :rescue_ran]
|
||||
end
|
||||
|
||||
it "will not execute an else block with ensure if an exception was raised in a method" do
|
||||
result = RescueSpecs.begin_else_ensure(true)
|
||||
result.should == :rescue_val
|
||||
ScratchPad.recorded.should == [:one, :rescue_ran, :ensure_ran]
|
||||
end
|
||||
|
||||
it "will not execute an else block but use the outer scope return value in a method" do
|
||||
result = RescueSpecs.begin_else_return(true)
|
||||
result.should == :return_val
|
||||
ScratchPad.recorded.should == [:one, :rescue_ran, :outside_begin]
|
||||
end
|
||||
|
||||
it "will not execute an else block with ensure but use the outer scope return value in a method" do
|
||||
result = RescueSpecs.begin_else_return_ensure(true)
|
||||
result.should == :return_val
|
||||
ScratchPad.recorded.should == [:one, :rescue_ran, :ensure_ran, :outside_begin]
|
||||
end
|
||||
|
||||
it "will not rescue errors raised in an else block in the rescue block above it" do
|
||||
lambda do
|
||||
begin
|
||||
ScratchPad << :one
|
||||
rescue Exception
|
||||
ScratchPad << :does_not_run
|
||||
else
|
||||
ScratchPad << :two
|
||||
raise SpecificExampleException, "an error from else"
|
||||
end
|
||||
end.should raise_error(SpecificExampleException)
|
||||
ScratchPad.recorded.should == [:one, :two]
|
||||
end
|
||||
|
||||
it "parses 'a += b rescue c' as 'a += (b rescue c)'" do
|
||||
a = 'a'
|
||||
c = 'c'
|
||||
a += b rescue c
|
||||
a.should == 'ac'
|
||||
end
|
||||
|
||||
it "without classes will not rescue Exception" do
|
||||
lambda do
|
||||
begin
|
||||
raise Exception
|
||||
rescue
|
||||
'Exception wrongly rescued'
|
||||
end
|
||||
end.should raise_error(Exception)
|
||||
end
|
||||
|
||||
it "uses === to compare against rescued classes" do
|
||||
rescuer = Class.new
|
||||
|
||||
def rescuer.===(exception)
|
||||
true
|
||||
end
|
||||
|
||||
begin
|
||||
raise Exception
|
||||
rescue rescuer
|
||||
rescued = :success
|
||||
rescue Exception
|
||||
rescued = :failure
|
||||
end
|
||||
|
||||
rescued.should == :success
|
||||
end
|
||||
|
||||
it "only accepts Module or Class in rescue clauses" do
|
||||
rescuer = 42
|
||||
lambda {
|
||||
begin
|
||||
raise "error"
|
||||
rescue rescuer
|
||||
end
|
||||
}.should raise_error(TypeError) { |e|
|
||||
e.message.should =~ /class or module required for rescue clause/
|
||||
}
|
||||
end
|
||||
|
||||
it "only accepts Module or Class in splatted rescue clauses" do
|
||||
rescuer = [42]
|
||||
lambda {
|
||||
begin
|
||||
raise "error"
|
||||
rescue *rescuer
|
||||
end
|
||||
}.should raise_error(TypeError) { |e|
|
||||
e.message.should =~ /class or module required for rescue clause/
|
||||
}
|
||||
end
|
||||
|
||||
it "evaluates rescue expressions only when needed" do
|
||||
invalid_rescuer = Object.new
|
||||
begin
|
||||
:foo
|
||||
rescue rescuer
|
||||
end.should == :foo
|
||||
end
|
||||
|
||||
it "should splat the handling Error classes" do
|
||||
begin
|
||||
raise "raise"
|
||||
rescue *(RuntimeError) => e
|
||||
:expected
|
||||
end.should == :expected
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue