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