opal 0.3.44 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +0 -1
- data/CHANGELOG.md +52 -0
- data/README.md +3 -3
- data/Rakefile +32 -8
- data/bin/opal +69 -16
- data/config.ru +1 -1
- data/examples/native/app/app.rb +28 -9
- data/examples/rack/app/app.rb +1 -1
- data/lib/opal.rb +0 -1
- data/lib/opal/cli.rb +106 -0
- data/lib/opal/lexer.rb +4 -2
- data/lib/opal/parser.rb +603 -360
- data/lib/opal/processor.rb +20 -8
- data/lib/opal/server.rb +47 -0
- data/lib/opal/source_map.rb +63 -0
- data/lib/opal/sprockets_parser.rb +77 -0
- data/lib/opal/sprockets_source_map_header.rb +21 -0
- data/lib/opal/target_scope.rb +14 -7
- data/lib/opal/version.rb +1 -1
- data/opal.gemspec +2 -0
- data/opal/opal-browser/script_loader.rb +7 -7
- data/opal/opal-parser.js.erb +2 -2
- data/opal/opal-source-maps.js.erb +2 -0
- data/opal/opal.rb +3 -4
- data/opal/opal/array.rb +31 -28
- data/opal/opal/boolean.rb +4 -0
- data/opal/opal/class.rb +14 -5
- data/opal/opal/enumerable.rb +68 -8
- data/opal/opal/error.rb +1 -1
- data/opal/opal/hash.rb +15 -18
- data/opal/opal/kernel.rb +24 -10
- data/opal/opal/native.rb +31 -0
- data/opal/opal/nil_class.rb +7 -2
- data/opal/opal/numeric.rb +10 -1
- data/opal/opal/proc.rb +4 -0
- data/opal/opal/range.rb +1 -1
- data/opal/opal/regexp.rb +13 -3
- data/opal/opal/runtime.js +134 -51
- data/opal/opal/string.rb +45 -22
- data/opal/opal/time.rb +25 -7
- data/opal/source_map.rb +63 -0
- data/opal/source_map/generator.rb +251 -0
- data/opal/source_map/parser.rb +102 -0
- data/opal/source_map/vlq.rb +122 -0
- data/opal/strscan.rb +30 -12
- data/spec/opal/class/_inherited_spec.rb +1 -1
- data/spec/{rubyspec/core → opal}/class/bridge_class_spec.rb +5 -3
- data/spec/{rubyspec/core → opal}/class/extend_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/class/instance_methods_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/class/last_value_spec.rb +0 -1
- data/spec/{rubyspec/core → opal}/json/parse_spec.rb +0 -0
- data/spec/{rubyspec/core/kernel/block_given.rb → opal/kernel/block_given_spec.rb} +0 -0
- data/spec/{rubyspec/core → opal}/kernel/class_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/kernel/extend_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/kernel/format_spec.rb +0 -0
- data/spec/opal/kernel/freeze_spec.rb +15 -0
- data/spec/{rubyspec/core → opal}/kernel/match_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/kernel/method_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/kernel/methods_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/kernel/nil_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/kernel/p_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/kernel/printf_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/kernel/proc_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/kernel/rand_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/kernel/respond_to_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/kernel/sprintf_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/kernel/to_json_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/module/alias_method_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/module/ancestors_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/module/append_features_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/module/constants_spec.rb +0 -0
- data/spec/{rubyspec/core → opal}/module/module_function_spec.rb +0 -1
- data/spec/opal/native_spec.rb +85 -3
- data/spec/opal/numeric/equal_spec.rb +9 -0
- data/spec/opal/parser/irb_spec.rb +43 -0
- data/spec/{rubyspec/core → opal}/proc/proc_tricks_spec.rb +0 -0
- data/spec/opal/runtime/block_send_spec.rb +28 -0
- data/spec/{rubyspec/core/runtime → opal/runtime2}/call_spec.rb +0 -0
- data/spec/{rubyspec/core/runtime → opal/runtime2}/class_hierarchy_spec.rb +0 -0
- data/spec/{rubyspec/core/runtime → opal/runtime2}/def_spec.rb +0 -0
- data/spec/{rubyspec/core/runtime → opal/runtime2}/defined_spec.rb +0 -0
- data/spec/{rubyspec/core/runtime → opal/runtime2}/super_spec.rb +0 -0
- data/spec/opal/source_map_spec.rb +19 -0
- data/spec/opal/string/freeze_spec.rb +15 -0
- data/spec/{rubyspec/core → opal}/string/to_json_spec.rb +0 -0
- data/spec/ospec/runner.rb +3 -0
- data/spec/parser/str_spec.rb +4 -0
- data/spec/rubyspec/core/enumerable/fixtures/classes.rb +2 -2
- data/spec/rubyspec/core/enumerable/none_spec.rb +68 -0
- data/spec/rubyspec/core/enumerable/sort_by_spec.rb +31 -0
- data/spec/rubyspec/core/hash/size_spec.rb +1 -1
- data/spec/rubyspec/core/hash/to_native_spec.rb +3 -3
- data/spec/rubyspec/core/string/fixtures/classes.rb +49 -0
- data/spec/rubyspec/core/string/index_spec.rb +405 -0
- data/spec/rubyspec/filters/bugs/language/class.rb +0 -2
- data/spec/rubyspec/filters/bugs/language/module.rb +3 -0
- data/spec/rubyspec/language/array_spec.rb +1 -1
- data/spec/rubyspec/language/block_spec.rb +1 -1
- data/spec/rubyspec/language/module_spec.rb +5 -5
- data/spec/rubyspec/language/predefined_spec.rb +1 -2
- data/spec/rubyspec/library/stringscanner/element_reference_spec.rb +29 -0
- data/spec/rubyspec/spec_helper.rb +31 -0
- metadata +130 -76
- data/lib/opal/erb.rb +0 -41
- data/opal/erb.rb +0 -19
- data/spec/opal/erb/erb_spec.rb +0 -31
- data/spec/simple_erb_template.opalerb +0 -1
- data/spec/templates/foo/bar.opalerb +0 -1
- data/spec/templates/prefixed.opalerb +0 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Opal::Parser do
|
|
4
|
+
describe "irb parser option" do
|
|
5
|
+
before do
|
|
6
|
+
@parser = Opal::Parser.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "creates Opal.irb_vars if it does not exist" do
|
|
10
|
+
$global["Opal"].irb_vars = nil
|
|
11
|
+
opal_eval_compiled(@parser.parse "nil", :irb => true)
|
|
12
|
+
|
|
13
|
+
($global["Opal"].irb_vars == nil).should be_false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "does not create Opal.irb_vars if :irb option not passed" do
|
|
17
|
+
$global["Opal"].irb_vars = nil
|
|
18
|
+
opal_eval_compiled(@parser.parse "nil")
|
|
19
|
+
|
|
20
|
+
($global["Opal"].irb_vars == nil).should be_true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "sets each s(:lasgn) in the top level onto irb_vars" do
|
|
24
|
+
opal_eval_compiled @parser.parse "foo = 42", :irb => true
|
|
25
|
+
$global["Opal"].irb_vars.foo.should == 42
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "gets each s(:lvar) in the top level from irb_vars" do
|
|
29
|
+
opal_eval_compiled @parser.parse "foo = 3.142; bar = foo", :irb => true
|
|
30
|
+
$global["Opal"].irb_vars.bar.should == 3.142
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "persists local vars between parses" do
|
|
34
|
+
opal_eval_compiled @parser.parse "foo = 'hello world'", :irb => true
|
|
35
|
+
opal_eval_compiled @parser.parse "bar = foo.upcase", :irb => true
|
|
36
|
+
$global["Opal"].irb_vars.bar.should == "HELLO WORLD"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "can still call top level methods" do
|
|
40
|
+
opal_eval_compiled(@parser.parse("to_s", :irb => true)).should == "main"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class RuntimeOpalBlockSendSpec
|
|
4
|
+
def simple
|
|
5
|
+
42
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def yielder(&block)
|
|
9
|
+
yield 3.142
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "Opal.block_send()" do
|
|
14
|
+
before do
|
|
15
|
+
@obj = RuntimeOpalBlockSendSpec.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "calls receiver with given method" do
|
|
19
|
+
`Opal.block_send(#{@obj}, 'simple')`.should == 42
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "calls method with given block" do
|
|
23
|
+
val = nil
|
|
24
|
+
p = proc { |a| val = a }
|
|
25
|
+
`Opal.block_send(#{@obj}, 'yielder', p)`
|
|
26
|
+
val.should == 3.142
|
|
27
|
+
end
|
|
28
|
+
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'opal-source-maps'
|
|
3
|
+
|
|
4
|
+
describe Opal::SourceMap do
|
|
5
|
+
before do
|
|
6
|
+
pathname = 'foo.rb'
|
|
7
|
+
@source = Opal.parse('1 + 1', pathname)
|
|
8
|
+
@map = Opal::SourceMap.new(@source, pathname)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'source has the magic comments' do
|
|
12
|
+
Opal::SourceMap::FILE_REGEXP.should =~ @source
|
|
13
|
+
Opal::SourceMap::LINE_REGEXP.should =~ @source
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'does not blow while generating the map' do
|
|
17
|
+
lambda { @map.as_json }.should_not raise_error
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Just accepting reality of immutability
|
|
2
|
+
|
|
3
|
+
describe "String#freeze" do
|
|
4
|
+
it "is always frozen" do
|
|
5
|
+
s = "a string"
|
|
6
|
+
s.frozen?.should be_true
|
|
7
|
+
s.freeze
|
|
8
|
+
s.frozen?.should be_true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "returns self" do
|
|
12
|
+
s = "a string"
|
|
13
|
+
s.freeze.should equal(s)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
File without changes
|
data/spec/ospec/runner.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
require 'date'
|
|
3
3
|
require 'observer'
|
|
4
4
|
|
|
5
|
+
ENV['MSPEC_RUNNER'] = true
|
|
6
|
+
|
|
5
7
|
class OSpecFilter
|
|
6
8
|
def self.main
|
|
7
9
|
@main ||= self.new
|
|
@@ -176,6 +178,7 @@ module Kernel
|
|
|
176
178
|
# FIXME: remove
|
|
177
179
|
def ruby_version_is(*); end
|
|
178
180
|
def pending(*); end
|
|
181
|
+
def language_version(*); end
|
|
179
182
|
end
|
|
180
183
|
|
|
181
184
|
module MSpec
|
data/spec/parser/str_spec.rb
CHANGED
|
@@ -99,5 +99,9 @@ describe "Strings" do
|
|
|
99
99
|
opal_parse("?a").should == [:str, "a"]
|
|
100
100
|
opal_parse("?&").should == [:str, "&"]
|
|
101
101
|
end
|
|
102
|
+
|
|
103
|
+
it "parses a string sexp as a command arg" do
|
|
104
|
+
opal_parse("foo ?a").should == [:call, nil, :foo, [:arglist, [:str, "a"]]]
|
|
105
|
+
end
|
|
102
106
|
end
|
|
103
107
|
end
|
|
@@ -120,14 +120,14 @@ module EnumerableSpecs
|
|
|
120
120
|
@values
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
class EnumConvertable
|
|
125
125
|
attr_accessor :called
|
|
126
126
|
attr_accessor :sym
|
|
127
127
|
def initialize(delegate)
|
|
128
128
|
@delegate = delegate
|
|
129
129
|
end
|
|
130
|
-
|
|
130
|
+
|
|
131
131
|
def to_enum(sym)
|
|
132
132
|
self.called = :to_enum
|
|
133
133
|
self.sym = sym
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
|
2
|
+
require File.expand_path('../fixtures/classes', __FILE__)
|
|
3
|
+
|
|
4
|
+
#ruby_version_is "1.8.7" do
|
|
5
|
+
describe "Enumerable#none?" do
|
|
6
|
+
it "returns true if none of the elements in self are true" do
|
|
7
|
+
e = EnumerableSpecs::Numerous.new(false, nil, false)
|
|
8
|
+
e.none?.should be_true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "returns false if at least one of the elements in self are true" do
|
|
12
|
+
e = EnumerableSpecs::Numerous.new(false, nil, true, false)
|
|
13
|
+
e.none?.should be_false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "gathers whole arrays as elements when each yields multiple" do
|
|
17
|
+
multi = EnumerableSpecs::YieldsMultiWithFalse.new
|
|
18
|
+
multi.none?.should be_false
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "Enumerable#none? with a block" do
|
|
23
|
+
before(:each) do
|
|
24
|
+
@e = EnumerableSpecs::Numerous.new(1,1,2,3,4)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "passes each element to the block in turn until it returns true" do
|
|
28
|
+
acc = []
|
|
29
|
+
@e.none? {|e| acc << e; false }
|
|
30
|
+
acc.should == [1,1,2,3,4]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "stops passing elements to the block when it returns true" do
|
|
34
|
+
acc = []
|
|
35
|
+
@e.none? {|e| acc << e; e == 3 ? true : false }
|
|
36
|
+
acc.should == [1,1,2,3]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "returns true if the block never returns true" do
|
|
40
|
+
@e.none? {|e| false }.should be_true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "returns false if the block ever returns true" do
|
|
44
|
+
@e.none? {|e| e == 3 ? true : false }.should be_false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
ruby_version_is ""..."1.9" do
|
|
48
|
+
it "gathers whole arrays as elements when each yields multiple" do
|
|
49
|
+
multi = EnumerableSpecs::YieldsMulti.new
|
|
50
|
+
multi.none? {|e| e == 1 }.should be_true
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
ruby_version_is "1.9" do
|
|
55
|
+
it "gathers initial args as elements when each yields multiple" do
|
|
56
|
+
multi = EnumerableSpecs::YieldsMulti.new
|
|
57
|
+
multi.none? {|e| e == [1, 2] }.should be_true
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "yields multiple arguments when each yields multiple" do
|
|
62
|
+
multi = EnumerableSpecs::YieldsMulti.new
|
|
63
|
+
yielded = []
|
|
64
|
+
multi.none? {|e, i| yielded << [e, i] }
|
|
65
|
+
yielded.should == [[1, 2]]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
#end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
|
2
|
+
require File.expand_path('../fixtures/classes', __FILE__)
|
|
3
|
+
|
|
4
|
+
describe "Enumerable#sort_by" do
|
|
5
|
+
it "returns an array of elements ordered by the result of block" do
|
|
6
|
+
a = EnumerableSpecs::Numerous.new("once", "upon", "a", "time")
|
|
7
|
+
a.sort_by { |i| i[0] }.should == ["a", "once", "time", "upon"]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "sorts the object by the given attribute" do
|
|
11
|
+
a = EnumerableSpecs::SortByDummy.new("fooo")
|
|
12
|
+
b = EnumerableSpecs::SortByDummy.new("bar")
|
|
13
|
+
|
|
14
|
+
ar = [a, b].sort_by { |d| d.s }
|
|
15
|
+
ar.should == [b, a]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
ruby_version_is "1.8.7" do
|
|
19
|
+
it "returns an Enumerator when a block is not supplied" do
|
|
20
|
+
a = EnumerableSpecs::Numerous.new("a","b")
|
|
21
|
+
a.sort_by.should be_an_instance_of(enumerator_class)
|
|
22
|
+
a.to_a.should == ["a", "b"]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "gathers whole arrays as elements when each yields multiple" do
|
|
27
|
+
# TODO: fix support for yielding multiple values (see fixtures/classes)
|
|
28
|
+
multi = EnumerableSpecs::YieldsMulti.new
|
|
29
|
+
multi.sort_by {|e| e.size}.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
describe "Hash#
|
|
1
|
+
describe "Hash#to_n" do
|
|
2
2
|
it "should return a js object representing hash" do
|
|
3
|
-
Hash.
|
|
3
|
+
Hash.new({:a => 100, :b => 200}.to_n).should == {:a => 100, :b => 200}
|
|
4
4
|
end
|
|
5
|
-
end
|
|
5
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
class Object
|
|
2
|
+
# This helper is defined here rather than in MSpec because
|
|
3
|
+
# it is only used in #unpack specs.
|
|
4
|
+
def unpack_format(count=nil, repeat=nil)
|
|
5
|
+
format = "#{instance_variable_get(:@method)}#{count}"
|
|
6
|
+
format *= repeat if repeat
|
|
7
|
+
format
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module StringSpecs
|
|
12
|
+
class MyString < String; end
|
|
13
|
+
class MyArray < Array; end
|
|
14
|
+
class MyRange < Range; end
|
|
15
|
+
|
|
16
|
+
class SubString < String
|
|
17
|
+
attr_reader :special
|
|
18
|
+
|
|
19
|
+
def initialize(str=nil)
|
|
20
|
+
@special = str
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class InitializeString < String
|
|
25
|
+
attr_reader :ivar
|
|
26
|
+
|
|
27
|
+
def initialize(other)
|
|
28
|
+
super
|
|
29
|
+
@ivar = 1
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def initialize_copy(other)
|
|
33
|
+
ScratchPad.record object_id
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module StringModule
|
|
38
|
+
def repr
|
|
39
|
+
1
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class StringWithRaisingConstructor < String
|
|
44
|
+
def initialize(str)
|
|
45
|
+
raise ArgumentError.new('constructor was called') unless str == 'silly:string'
|
|
46
|
+
self.replace(str)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
|
3
|
+
require File.expand_path('../fixtures/classes.rb', __FILE__)
|
|
4
|
+
|
|
5
|
+
describe "String#index" do
|
|
6
|
+
it "raises a TypeError if passed nil" do
|
|
7
|
+
lambda { "abc".index nil }.should raise_error(TypeError)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "raises a TypeError if passed a boolean" do
|
|
11
|
+
lambda { "abc".index true }.should raise_error(TypeError)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
not_compliant_on :opal do
|
|
15
|
+
it "raises a TypeError if passed a Symbol" do
|
|
16
|
+
lambda { "abc".index :a }.should raise_error(TypeError)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "calls #to_str to convert the first argument" do
|
|
20
|
+
char = mock("string index char")
|
|
21
|
+
char.should_receive(:to_str).and_return("b")
|
|
22
|
+
"abc".index(char).should == 1
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "calls #to_int to convert the second argument" do
|
|
26
|
+
offset = mock("string index offset")
|
|
27
|
+
offset.should_receive(:to_int).and_return(1)
|
|
28
|
+
"abc".index("c", offset).should == 2
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
ruby_version_is ""..."1.9" do
|
|
33
|
+
it "does not call #to_int to convert the first argument" do
|
|
34
|
+
char = mock("string index char")
|
|
35
|
+
char.should_not_receive(:to_int)
|
|
36
|
+
lambda { "abc".index char }.should raise_error(TypeError)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
ruby_version_is "1.9" do
|
|
41
|
+
it "raises a TypeError if passed a Fixnum" do
|
|
42
|
+
lambda { "abc".index 97 }.should raise_error(TypeError)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
ruby_version_is ""..."1.9" do
|
|
48
|
+
describe "String#index with Fixnum" do
|
|
49
|
+
it "returns the index of the first occurrence of the given character" do
|
|
50
|
+
"hello".index(?e).should == 1
|
|
51
|
+
"hello".index(?l).should == 2
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "character values over 255 (256th ASCII character) always result in nil" do
|
|
55
|
+
# A naive implementation could try to use % 256
|
|
56
|
+
"hello".index(?e + 256 * 3).should == nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "negative character values always result in nil" do
|
|
60
|
+
# A naive implementation could try to use % 256
|
|
61
|
+
"hello".index(-(256 - ?e)).should == nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "starts the search at the given offset" do
|
|
65
|
+
"blablabla".index(?b, 0).should == 0
|
|
66
|
+
"blablabla".index(?b, 1).should == 3
|
|
67
|
+
"blablabla".index(?b, 2).should == 3
|
|
68
|
+
"blablabla".index(?b, 3).should == 3
|
|
69
|
+
"blablabla".index(?b, 4).should == 6
|
|
70
|
+
"blablabla".index(?b, 5).should == 6
|
|
71
|
+
"blablabla".index(?b, 6).should == 6
|
|
72
|
+
|
|
73
|
+
"blablabla".index(?a, 0).should == 2
|
|
74
|
+
"blablabla".index(?a, 2).should == 2
|
|
75
|
+
"blablabla".index(?a, 3).should == 5
|
|
76
|
+
"blablabla".index(?a, 4).should == 5
|
|
77
|
+
"blablabla".index(?a, 5).should == 5
|
|
78
|
+
"blablabla".index(?a, 6).should == 8
|
|
79
|
+
"blablabla".index(?a, 7).should == 8
|
|
80
|
+
"blablabla".index(?a, 8).should == 8
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "starts the search at offset + self.length if offset is negative" do
|
|
84
|
+
str = "blablabla"
|
|
85
|
+
|
|
86
|
+
[?a, ?b].each do |needle|
|
|
87
|
+
(-str.length .. -1).each do |offset|
|
|
88
|
+
p offset
|
|
89
|
+
str.index(needle, offset).should ==
|
|
90
|
+
str.index(needle, offset + str.length)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
"blablabla".index(?b, -9).should == 0
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "returns nil if offset + self.length is < 0 for negative offsets" do
|
|
98
|
+
"blablabla".index(?b, -10).should == nil
|
|
99
|
+
"blablabla".index(?b, -20).should == nil
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "returns nil if the character isn't found" do
|
|
103
|
+
"hello".index(0).should == nil
|
|
104
|
+
|
|
105
|
+
"hello".index(?H).should == nil
|
|
106
|
+
"hello".index(?z).should == nil
|
|
107
|
+
"hello".index(?e, 2).should == nil
|
|
108
|
+
|
|
109
|
+
"blablabla".index(?b, 7).should == nil
|
|
110
|
+
"blablabla".index(?b, 10).should == nil
|
|
111
|
+
|
|
112
|
+
"blablabla".index(?a, 9).should == nil
|
|
113
|
+
"blablabla".index(?a, 20).should == nil
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe "String#index with String" do
|
|
119
|
+
it "behaves the same as String#index(char) for one-character strings" do
|
|
120
|
+
["blablabla", "hello cruel world...!"].each do |str|
|
|
121
|
+
str.split("").uniq.each do |str|
|
|
122
|
+
chr = str[0]
|
|
123
|
+
str.index(str).should == str.index(chr)
|
|
124
|
+
|
|
125
|
+
0.upto(str.size + 1) do |start|
|
|
126
|
+
str.index(str, start).should == str.index(chr, start)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
(-str.size - 1).upto(-1) do |start|
|
|
130
|
+
str.index(str, start).should == str.index(chr, start)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "returns the index of the first occurrence of the given substring" do
|
|
137
|
+
"blablabla".index("").should == 0
|
|
138
|
+
"blablabla".index("b").should == 0
|
|
139
|
+
"blablabla".index("bla").should == 0
|
|
140
|
+
"blablabla".index("blabla").should == 0
|
|
141
|
+
"blablabla".index("blablabla").should == 0
|
|
142
|
+
|
|
143
|
+
"blablabla".index("l").should == 1
|
|
144
|
+
"blablabla".index("la").should == 1
|
|
145
|
+
"blablabla".index("labla").should == 1
|
|
146
|
+
"blablabla".index("lablabla").should == 1
|
|
147
|
+
|
|
148
|
+
"blablabla".index("a").should == 2
|
|
149
|
+
"blablabla".index("abla").should == 2
|
|
150
|
+
"blablabla".index("ablabla").should == 2
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "doesn't set $~" do
|
|
154
|
+
$~ = nil
|
|
155
|
+
|
|
156
|
+
'hello.'.index('ll')
|
|
157
|
+
$~.should == nil
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "ignores string subclasses" do
|
|
161
|
+
"blablabla".index(StringSpecs::MyString.new("bla")).should == 0
|
|
162
|
+
StringSpecs::MyString.new("blablabla").index("bla").should == 0
|
|
163
|
+
StringSpecs::MyString.new("blablabla").index(StringSpecs::MyString.new("bla")).should == 0
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "starts the search at the given offset" do
|
|
167
|
+
"blablabla".index("bl", 0).should == 0
|
|
168
|
+
"blablabla".index("bl", 1).should == 3
|
|
169
|
+
"blablabla".index("bl", 2).should == 3
|
|
170
|
+
"blablabla".index("bl", 3).should == 3
|
|
171
|
+
|
|
172
|
+
"blablabla".index("bla", 0).should == 0
|
|
173
|
+
"blablabla".index("bla", 1).should == 3
|
|
174
|
+
"blablabla".index("bla", 2).should == 3
|
|
175
|
+
"blablabla".index("bla", 3).should == 3
|
|
176
|
+
|
|
177
|
+
"blablabla".index("blab", 0).should == 0
|
|
178
|
+
"blablabla".index("blab", 1).should == 3
|
|
179
|
+
"blablabla".index("blab", 2).should == 3
|
|
180
|
+
"blablabla".index("blab", 3).should == 3
|
|
181
|
+
|
|
182
|
+
"blablabla".index("la", 1).should == 1
|
|
183
|
+
"blablabla".index("la", 2).should == 4
|
|
184
|
+
"blablabla".index("la", 3).should == 4
|
|
185
|
+
"blablabla".index("la", 4).should == 4
|
|
186
|
+
|
|
187
|
+
"blablabla".index("lab", 1).should == 1
|
|
188
|
+
"blablabla".index("lab", 2).should == 4
|
|
189
|
+
"blablabla".index("lab", 3).should == 4
|
|
190
|
+
"blablabla".index("lab", 4).should == 4
|
|
191
|
+
|
|
192
|
+
"blablabla".index("ab", 2).should == 2
|
|
193
|
+
"blablabla".index("ab", 3).should == 5
|
|
194
|
+
"blablabla".index("ab", 4).should == 5
|
|
195
|
+
"blablabla".index("ab", 5).should == 5
|
|
196
|
+
|
|
197
|
+
"blablabla".index("", 0).should == 0
|
|
198
|
+
"blablabla".index("", 1).should == 1
|
|
199
|
+
"blablabla".index("", 2).should == 2
|
|
200
|
+
"blablabla".index("", 7).should == 7
|
|
201
|
+
"blablabla".index("", 8).should == 8
|
|
202
|
+
"blablabla".index("", 9).should == 9
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it "starts the search at offset + self.length if offset is negative" do
|
|
206
|
+
str = "blablabla"
|
|
207
|
+
|
|
208
|
+
["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
|
|
209
|
+
(-str.length .. -1).each do |offset|
|
|
210
|
+
str.index(needle, offset).should ==
|
|
211
|
+
str.index(needle, offset + str.length)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
it "returns nil if the substring isn't found" do
|
|
217
|
+
"blablabla".index("B").should == nil
|
|
218
|
+
"blablabla".index("z").should == nil
|
|
219
|
+
"blablabla".index("BLA").should == nil
|
|
220
|
+
"blablabla".index("blablablabla").should == nil
|
|
221
|
+
"blablabla".index("", 10).should == nil
|
|
222
|
+
"blablabla".index("", 10).should == nil
|
|
223
|
+
|
|
224
|
+
"12345".index("", 6).should == nil
|
|
225
|
+
"hello".index("he", 1).should == nil
|
|
226
|
+
"hello".index("he", 2).should == nil
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
with_feature :encoding do
|
|
230
|
+
it "returns the character index of a multibyte character" do
|
|
231
|
+
"ありがとう".index("が").should == 2
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
it "returns the character index after offset" do
|
|
235
|
+
"われわれ".index("わ", 1).should == 2
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
|
|
239
|
+
char = "れ".encode Encoding::EUC_JP
|
|
240
|
+
lambda do
|
|
241
|
+
"あれ".index char
|
|
242
|
+
end.should raise_error(Encoding::CompatibilityError)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
describe "String#index with Regexp" do
|
|
248
|
+
it "behaves the same as String#index(string) for escaped string regexps" do
|
|
249
|
+
["blablabla", "hello cruel world...!"].each do |str|
|
|
250
|
+
["", "b", "bla", "lab", "o c", "d."].each do |needle|
|
|
251
|
+
regexp = Regexp.new(Regexp.escape(needle))
|
|
252
|
+
str.index(regexp).should == str.index(needle)
|
|
253
|
+
|
|
254
|
+
0.upto(str.size + 1) do |start|
|
|
255
|
+
str.index(regexp, start).should == str.index(needle, start)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
(-str.size - 1).upto(-1) do |start|
|
|
259
|
+
str.index(regexp, start).should == str.index(needle, start)
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
it "returns the index of the first match of regexp" do
|
|
266
|
+
"blablabla".index(/bla/).should == 0
|
|
267
|
+
"blablabla".index(/BLA/i).should == 0
|
|
268
|
+
|
|
269
|
+
"blablabla".index(/.{0}/).should == 0
|
|
270
|
+
"blablabla".index(/.{6}/).should == 0
|
|
271
|
+
"blablabla".index(/.{9}/).should == 0
|
|
272
|
+
|
|
273
|
+
"blablabla".index(/.*/).should == 0
|
|
274
|
+
"blablabla".index(/.+/).should == 0
|
|
275
|
+
|
|
276
|
+
"blablabla".index(/lab|b/).should == 0
|
|
277
|
+
|
|
278
|
+
not_compliant_on :opal do
|
|
279
|
+
"blablabla".index(/\A/).should == 0
|
|
280
|
+
"blablabla".index(/\Z/).should == 9
|
|
281
|
+
"blablabla".index(/\z/).should == 9
|
|
282
|
+
"blablabla\n".index(/\Z/).should == 9
|
|
283
|
+
"blablabla\n".index(/\z/).should == 10
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
"blablabla".index(/^/).should == 0
|
|
287
|
+
"\nblablabla".index(/^/).should == 0
|
|
288
|
+
|
|
289
|
+
not_compliant_on :opal do
|
|
290
|
+
"b\nablabla".index(/$/).should == 1
|
|
291
|
+
"bl\nablabla".index(/$/).should == 2
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
"blablabla".index(/.l./).should == 0
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
it "sets $~ to MatchData of match and nil when there's none" do
|
|
298
|
+
'hello.'.index(/.(.)/)
|
|
299
|
+
$~[0].should == 'he'
|
|
300
|
+
|
|
301
|
+
'hello.'.index(/not/)
|
|
302
|
+
$~.should == nil
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
it "starts the search at the given offset" do
|
|
306
|
+
"blablabla".index(/.{0}/, 5).should == 5
|
|
307
|
+
"blablabla".index(/.{1}/, 5).should == 5
|
|
308
|
+
"blablabla".index(/.{2}/, 5).should == 5
|
|
309
|
+
"blablabla".index(/.{3}/, 5).should == 5
|
|
310
|
+
"blablabla".index(/.{4}/, 5).should == 5
|
|
311
|
+
|
|
312
|
+
"blablabla".index(/.{0}/, 3).should == 3
|
|
313
|
+
"blablabla".index(/.{1}/, 3).should == 3
|
|
314
|
+
"blablabla".index(/.{2}/, 3).should == 3
|
|
315
|
+
"blablabla".index(/.{5}/, 3).should == 3
|
|
316
|
+
"blablabla".index(/.{6}/, 3).should == 3
|
|
317
|
+
|
|
318
|
+
"blablabla".index(/.l./, 0).should == 0
|
|
319
|
+
"blablabla".index(/.l./, 1).should == 3
|
|
320
|
+
"blablabla".index(/.l./, 2).should == 3
|
|
321
|
+
"blablabla".index(/.l./, 3).should == 3
|
|
322
|
+
|
|
323
|
+
"xblaxbla".index(/x./, 0).should == 0
|
|
324
|
+
"xblaxbla".index(/x./, 1).should == 4
|
|
325
|
+
"xblaxbla".index(/x./, 2).should == 4
|
|
326
|
+
|
|
327
|
+
not_compliant_on :opal do
|
|
328
|
+
"blablabla\n".index(/\Z/, 9).should == 9
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
it "starts the search at offset + self.length if offset is negative" do
|
|
333
|
+
str = "blablabla"
|
|
334
|
+
|
|
335
|
+
["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
|
|
336
|
+
(-str.length .. -1).each do |offset|
|
|
337
|
+
str.index(needle, offset).should ==
|
|
338
|
+
str.index(needle, offset + str.length)
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
it "returns nil if the substring isn't found" do
|
|
344
|
+
"blablabla".index(/BLA/).should == nil
|
|
345
|
+
|
|
346
|
+
"blablabla".index(/.{10}/).should == nil
|
|
347
|
+
"blaxbla".index(/.x/, 3).should == nil
|
|
348
|
+
"blaxbla".index(/..x/, 2).should == nil
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
it "returns nil if the Regexp matches the empty string and the offset is out of range" do
|
|
352
|
+
"ruby".index(//,12).should be_nil
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
not_compliant_on :opal do
|
|
356
|
+
it "supports \\G which matches at the given start offset" do
|
|
357
|
+
"helloYOU.".index(/\GYOU/, 5).should == 5
|
|
358
|
+
"helloYOU.".index(/\GYOU/).should == nil
|
|
359
|
+
|
|
360
|
+
re = /\G.+YOU/
|
|
361
|
+
# The # marks where \G will match.
|
|
362
|
+
[
|
|
363
|
+
["#hi!YOUall.", 0],
|
|
364
|
+
["h#i!YOUall.", 1],
|
|
365
|
+
["hi#!YOUall.", 2],
|
|
366
|
+
["hi!#YOUall.", nil]
|
|
367
|
+
].each do |spec|
|
|
368
|
+
|
|
369
|
+
start = spec[0].index("#")
|
|
370
|
+
str = spec[0].delete("#")
|
|
371
|
+
|
|
372
|
+
str.index(re, start).should == spec[1]
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
not_compliant_on :opal do
|
|
378
|
+
it "converts start_offset to an integer via to_int" do
|
|
379
|
+
obj = mock('1')
|
|
380
|
+
obj.should_receive(:to_int).and_return(1)
|
|
381
|
+
"RWOARW".index(/R./, obj).should == 4
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
with_feature :encoding do
|
|
386
|
+
it "returns the character index of a multibyte character" do
|
|
387
|
+
"ありがとう".index(/が/).should == 2
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
it "returns the character index after offset" do
|
|
391
|
+
"われわれ".index(/わ/, 1).should == 2
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
it "treats the offset as a character index" do
|
|
395
|
+
"われわわれ".index(/わ/, 3).should == 3
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
|
|
399
|
+
re = Regexp.new "れ".encode(Encoding::EUC_JP)
|
|
400
|
+
lambda do
|
|
401
|
+
"あれ".index re
|
|
402
|
+
end.should raise_error(Encoding::CompatibilityError)
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
end
|