1
0
Fork 0
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:
eregon 2017-09-20 20:18:52 +00:00
parent 75bfc6440d
commit 1d15d5f080
4370 changed files with 0 additions and 0 deletions

View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

View 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

View 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

View 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

View file

@ -0,0 +1 @@
p ARGV.map { |a| a.encoding.name }

View 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

View 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

View 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

View file

@ -0,0 +1,9 @@
print "a,"
print lambda {
print "b,"
break "break,"
print "c,"
}.call
puts "d"

View 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"

View 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"

View 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

View file

@ -0,0 +1,11 @@
# encoding: us-ascii
module CodingUS_ASCII
def self.encoding
__ENCODING__
end
def self.string_literal
"string literal"
end
end

View file

@ -0,0 +1,11 @@
# encoding: utf-8
module CodingUTF_8
def self.encoding
__ENCODING__
end
def self.string_literal
"string literal"
end
end

View 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

View 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

View file

@ -0,0 +1,8 @@
def some_toplevel_method
end
public
def public_toplevel_method
end
private

View 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

View file

@ -0,0 +1,6 @@
puts $0
puts __FILE__
if $0 == __FILE__
print "OK"
end

View 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

View file

@ -0,0 +1 @@
ScratchPad.record __FILE__

View file

@ -0,0 +1,5 @@
# frozen_string_literal: true
require_relative 'freeze_magic_comment_required'
p "abc".object_id == $second_literal_id

View file

@ -0,0 +1,5 @@
# frozen_string_literal: true
require_relative 'freeze_magic_comment_required_diff_enc'
p "abc".object_id != $second_literal_id

View file

@ -0,0 +1,5 @@
# frozen_string_literal: true
require_relative 'freeze_magic_comment_required_no_comment'
p "abc".object_id != $second_literal_id

View file

@ -0,0 +1,4 @@
# frozen_string_literal: true
ids = Array.new(2) { "abc".object_id }
p ids.first == ids.last

View file

@ -0,0 +1,3 @@
# frozen_string_literal: true
$second_literal_id = "abc".object_id

View file

@ -0,0 +1 @@
$second_literal_id = "abc".object_id

View file

@ -0,0 +1,3 @@
# frozen_string_literal: true
p "abc".object_id == "abc".object_id

View file

@ -0,0 +1,7 @@
# encoding: ascii-8bit
module HashStringsASCII8BIT
def self.literal_hash
{"foo" => "bar"}
end
end

View file

@ -0,0 +1,7 @@
# encoding: us-ascii
module HashStringsUSASCII
def self.literal_hash
{"foo" => "bar"}
end
end

View file

@ -0,0 +1,7 @@
# encoding: utf-8
module HashStringsUTF8
def self.literal_hash
{"foo" => "bar"}
end
end

View file

@ -0,0 +1,9 @@
class OperatorImplementor
def =~(val)
return val
end
def !~(val)
return val
end
end

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View file

@ -0,0 +1,4 @@
puts Object.const_defined?(:DATA)
__END__
data1

View file

@ -0,0 +1,4 @@
require File.expand_path("../data4.rb", __FILE__)
p Object.const_defined?(:DATA)

View file

@ -0,0 +1,7 @@
require File.expand_path("../data4.rb", __FILE__)
puts DATA.read
__END__
data 3

View file

@ -0,0 +1,4 @@
# nothing
__END__
data 4

View file

@ -0,0 +1,5 @@
DATA.rewind
puts DATA.gets
__END__
data 5

View file

@ -0,0 +1,2 @@
__END__
data only

View file

@ -0,0 +1,3 @@
at_exit {
puts DATA.read
}

File diff suppressed because it is too large Load diff

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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