opal 0.3.44 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/.travis.yml +0 -1
  2. data/CHANGELOG.md +52 -0
  3. data/README.md +3 -3
  4. data/Rakefile +32 -8
  5. data/bin/opal +69 -16
  6. data/config.ru +1 -1
  7. data/examples/native/app/app.rb +28 -9
  8. data/examples/rack/app/app.rb +1 -1
  9. data/lib/opal.rb +0 -1
  10. data/lib/opal/cli.rb +106 -0
  11. data/lib/opal/lexer.rb +4 -2
  12. data/lib/opal/parser.rb +603 -360
  13. data/lib/opal/processor.rb +20 -8
  14. data/lib/opal/server.rb +47 -0
  15. data/lib/opal/source_map.rb +63 -0
  16. data/lib/opal/sprockets_parser.rb +77 -0
  17. data/lib/opal/sprockets_source_map_header.rb +21 -0
  18. data/lib/opal/target_scope.rb +14 -7
  19. data/lib/opal/version.rb +1 -1
  20. data/opal.gemspec +2 -0
  21. data/opal/opal-browser/script_loader.rb +7 -7
  22. data/opal/opal-parser.js.erb +2 -2
  23. data/opal/opal-source-maps.js.erb +2 -0
  24. data/opal/opal.rb +3 -4
  25. data/opal/opal/array.rb +31 -28
  26. data/opal/opal/boolean.rb +4 -0
  27. data/opal/opal/class.rb +14 -5
  28. data/opal/opal/enumerable.rb +68 -8
  29. data/opal/opal/error.rb +1 -1
  30. data/opal/opal/hash.rb +15 -18
  31. data/opal/opal/kernel.rb +24 -10
  32. data/opal/opal/native.rb +31 -0
  33. data/opal/opal/nil_class.rb +7 -2
  34. data/opal/opal/numeric.rb +10 -1
  35. data/opal/opal/proc.rb +4 -0
  36. data/opal/opal/range.rb +1 -1
  37. data/opal/opal/regexp.rb +13 -3
  38. data/opal/opal/runtime.js +134 -51
  39. data/opal/opal/string.rb +45 -22
  40. data/opal/opal/time.rb +25 -7
  41. data/opal/source_map.rb +63 -0
  42. data/opal/source_map/generator.rb +251 -0
  43. data/opal/source_map/parser.rb +102 -0
  44. data/opal/source_map/vlq.rb +122 -0
  45. data/opal/strscan.rb +30 -12
  46. data/spec/opal/class/_inherited_spec.rb +1 -1
  47. data/spec/{rubyspec/core → opal}/class/bridge_class_spec.rb +5 -3
  48. data/spec/{rubyspec/core → opal}/class/extend_spec.rb +0 -0
  49. data/spec/{rubyspec/core → opal}/class/instance_methods_spec.rb +0 -0
  50. data/spec/{rubyspec/core → opal}/class/last_value_spec.rb +0 -1
  51. data/spec/{rubyspec/core → opal}/json/parse_spec.rb +0 -0
  52. data/spec/{rubyspec/core/kernel/block_given.rb → opal/kernel/block_given_spec.rb} +0 -0
  53. data/spec/{rubyspec/core → opal}/kernel/class_spec.rb +0 -0
  54. data/spec/{rubyspec/core → opal}/kernel/extend_spec.rb +0 -0
  55. data/spec/{rubyspec/core → opal}/kernel/format_spec.rb +0 -0
  56. data/spec/opal/kernel/freeze_spec.rb +15 -0
  57. data/spec/{rubyspec/core → opal}/kernel/match_spec.rb +0 -0
  58. data/spec/{rubyspec/core → opal}/kernel/method_spec.rb +0 -0
  59. data/spec/{rubyspec/core → opal}/kernel/methods_spec.rb +0 -0
  60. data/spec/{rubyspec/core → opal}/kernel/nil_spec.rb +0 -0
  61. data/spec/{rubyspec/core → opal}/kernel/p_spec.rb +0 -0
  62. data/spec/{rubyspec/core → opal}/kernel/printf_spec.rb +0 -0
  63. data/spec/{rubyspec/core → opal}/kernel/proc_spec.rb +0 -0
  64. data/spec/{rubyspec/core → opal}/kernel/rand_spec.rb +0 -0
  65. data/spec/{rubyspec/core → opal}/kernel/respond_to_spec.rb +0 -0
  66. data/spec/{rubyspec/core → opal}/kernel/sprintf_spec.rb +0 -0
  67. data/spec/{rubyspec/core → opal}/kernel/to_json_spec.rb +0 -0
  68. data/spec/{rubyspec/core → opal}/module/alias_method_spec.rb +0 -0
  69. data/spec/{rubyspec/core → opal}/module/ancestors_spec.rb +0 -0
  70. data/spec/{rubyspec/core → opal}/module/append_features_spec.rb +0 -0
  71. data/spec/{rubyspec/core → opal}/module/constants_spec.rb +0 -0
  72. data/spec/{rubyspec/core → opal}/module/module_function_spec.rb +0 -1
  73. data/spec/opal/native_spec.rb +85 -3
  74. data/spec/opal/numeric/equal_spec.rb +9 -0
  75. data/spec/opal/parser/irb_spec.rb +43 -0
  76. data/spec/{rubyspec/core → opal}/proc/proc_tricks_spec.rb +0 -0
  77. data/spec/opal/runtime/block_send_spec.rb +28 -0
  78. data/spec/{rubyspec/core/runtime → opal/runtime2}/call_spec.rb +0 -0
  79. data/spec/{rubyspec/core/runtime → opal/runtime2}/class_hierarchy_spec.rb +0 -0
  80. data/spec/{rubyspec/core/runtime → opal/runtime2}/def_spec.rb +0 -0
  81. data/spec/{rubyspec/core/runtime → opal/runtime2}/defined_spec.rb +0 -0
  82. data/spec/{rubyspec/core/runtime → opal/runtime2}/super_spec.rb +0 -0
  83. data/spec/opal/source_map_spec.rb +19 -0
  84. data/spec/opal/string/freeze_spec.rb +15 -0
  85. data/spec/{rubyspec/core → opal}/string/to_json_spec.rb +0 -0
  86. data/spec/ospec/runner.rb +3 -0
  87. data/spec/parser/str_spec.rb +4 -0
  88. data/spec/rubyspec/core/enumerable/fixtures/classes.rb +2 -2
  89. data/spec/rubyspec/core/enumerable/none_spec.rb +68 -0
  90. data/spec/rubyspec/core/enumerable/sort_by_spec.rb +31 -0
  91. data/spec/rubyspec/core/hash/size_spec.rb +1 -1
  92. data/spec/rubyspec/core/hash/to_native_spec.rb +3 -3
  93. data/spec/rubyspec/core/string/fixtures/classes.rb +49 -0
  94. data/spec/rubyspec/core/string/index_spec.rb +405 -0
  95. data/spec/rubyspec/filters/bugs/language/class.rb +0 -2
  96. data/spec/rubyspec/filters/bugs/language/module.rb +3 -0
  97. data/spec/rubyspec/language/array_spec.rb +1 -1
  98. data/spec/rubyspec/language/block_spec.rb +1 -1
  99. data/spec/rubyspec/language/module_spec.rb +5 -5
  100. data/spec/rubyspec/language/predefined_spec.rb +1 -2
  101. data/spec/rubyspec/library/stringscanner/element_reference_spec.rb +29 -0
  102. data/spec/rubyspec/spec_helper.rb +31 -0
  103. metadata +130 -76
  104. data/lib/opal/erb.rb +0 -41
  105. data/opal/erb.rb +0 -19
  106. data/spec/opal/erb/erb_spec.rb +0 -31
  107. data/spec/simple_erb_template.opalerb +0 -1
  108. data/spec/templates/foo/bar.opalerb +0 -1
  109. 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -7,4 +7,4 @@ describe "Hash#size" do
7
7
  Hash.new(5).size.should == 0
8
8
  Hash.new { 5 }.size.should == 0
9
9
  end
10
- end
10
+ end
@@ -1,5 +1,5 @@
1
- describe "Hash#to_native" do
1
+ describe "Hash#to_n" do
2
2
  it "should return a js object representing hash" do
3
- Hash.from_native({:a => 100, :b => 200}.to_native).should == {:a => 100, :b => 200}
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