opal 0.10.0.beta2 → 0.10.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/lib/opal/compiler.rb +15 -9
- data/lib/opal/fragment.rb +8 -1
- data/lib/opal/nodes/args/restarg.rb +6 -1
- data/lib/opal/nodes/base.rb +1 -1
- data/lib/opal/nodes/call.rb +4 -0
- data/lib/opal/nodes/def.rb +20 -25
- data/lib/opal/nodes/hash.rb +89 -17
- data/lib/opal/nodes/iter.rb +30 -2
- data/lib/opal/nodes/logic.rb +54 -4
- data/lib/opal/nodes/node_with_args.rb +72 -0
- data/lib/opal/parser.rb +16 -0
- data/lib/opal/parser/grammar.rb +2555 -2562
- data/lib/opal/parser/grammar.y +28 -20
- data/lib/opal/parser/lexer.rb +4 -4
- data/lib/opal/regexp_anchors.rb +13 -5
- data/lib/opal/source_map.rb +2 -1
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/array.rb +4 -0
- data/opal/corelib/basic_object.rb +3 -1
- data/opal/corelib/constants.rb +1 -1
- data/opal/corelib/file.rb +196 -4
- data/opal/corelib/hash.rb +7 -7
- data/opal/corelib/kernel.rb +7 -4
- data/opal/corelib/marshal.rb +31 -0
- data/opal/corelib/marshal/read_buffer.rb +427 -0
- data/opal/corelib/marshal/write_buffer.rb +383 -0
- data/opal/corelib/method.rb +8 -0
- data/opal/corelib/module.rb +21 -0
- data/opal/corelib/number.rb +19 -5
- data/opal/corelib/proc.rb +33 -6
- data/opal/corelib/range.rb +6 -0
- data/opal/corelib/regexp.rb +5 -1
- data/opal/corelib/runtime.js +69 -17
- data/opal/corelib/string.rb +8 -0
- data/opal/corelib/string/inheritance.rb +4 -0
- data/opal/corelib/struct.rb +5 -0
- data/opal/corelib/unsupported.rb +0 -18
- data/opal/opal/full.rb +1 -0
- data/spec/filters/bugs/basicobject.rb +0 -2
- data/spec/filters/bugs/compiler_opal.rb +5 -0
- data/spec/filters/bugs/enumerable.rb +1 -0
- data/spec/filters/bugs/enumerator.rb +0 -2
- data/spec/filters/bugs/exception.rb +0 -1
- data/spec/filters/bugs/kernel.rb +0 -5
- data/spec/filters/bugs/language.rb +7 -27
- data/spec/filters/bugs/marshal.rb +43 -0
- data/spec/filters/bugs/method.rb +0 -56
- data/spec/filters/bugs/module.rb +0 -1
- data/spec/filters/bugs/proc.rb +0 -46
- data/spec/filters/bugs/regexp.rb +1 -0
- data/spec/filters/bugs/unboundmethod.rb +0 -13
- data/spec/filters/unsupported/bignum.rb +5 -0
- data/spec/filters/unsupported/freeze.rb +2 -0
- data/spec/filters/unsupported/marshal.rb +46 -0
- data/spec/filters/unsupported/symbol.rb +5 -0
- data/spec/lib/compiler/call_spec.rb +29 -29
- data/spec/lib/compiler_spec.rb +7 -1
- data/spec/opal/core/kernel/instance_variables_spec.rb +40 -0
- data/spec/opal/core/language/ternary_operator_spec.rb +6 -0
- data/spec/opal/core/marshal/dump_spec.rb +53 -0
- data/spec/opal/core/marshal/load_spec.rb +7 -0
- data/spec/opal/core/source_map_spec.rb +35 -1
- data/spec/opal/javascript_api_spec.rb +16 -0
- data/spec/opal/stdlib/source_map_spec.rb +8 -0
- data/spec/ruby_specs +7 -4
- data/spec/support/match_helpers.rb +57 -0
- data/spec/support/mspec_rspec_adapter.rb +1 -1
- data/stdlib/opal-parser.rb +3 -1
- data/stdlib/pathname.rb +105 -7
- data/stdlib/racc/parser.rb +551 -138
- data/stdlib/source_map/vlq.rb +3 -2
- data/tasks/testing.rake +4 -2
- metadata +22 -2
data/spec/lib/compiler_spec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'lib/spec_helper'
|
2
|
+
require 'support/match_helpers'
|
2
3
|
|
3
4
|
describe Opal::Compiler do
|
4
5
|
describe 'requiring' do
|
@@ -20,6 +21,10 @@ describe Opal::Compiler do
|
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
24
|
+
it 'raises syntax errors properly' do
|
25
|
+
expect(lambda {Opal::Compiler.new('def foo').compile}).to raise_error SyntaxError, /An error occurred while compiling:.*false/m
|
26
|
+
end
|
27
|
+
|
23
28
|
it "should compile simple ruby values" do
|
24
29
|
expect_compiled("3.142").to include("return 3.142")
|
25
30
|
expect_compiled("123e1").to include("return 1230")
|
@@ -143,9 +148,10 @@ describe Opal::Compiler do
|
|
143
148
|
|
144
149
|
describe '#require_tree' do
|
145
150
|
require 'pathname'
|
146
|
-
let(:file) { Pathname(__FILE__).join('../fixtures/require_tree_test.rb') }
|
147
151
|
|
148
152
|
it 'parses and resolve #require argument' do
|
153
|
+
file = Pathname(__FILE__).join('../fixtures/require_tree_test.rb')
|
154
|
+
|
149
155
|
compiler = compiler_for(file.read)
|
150
156
|
expect(compiler.required_trees).to eq(['../fixtures/required_tree_test'])
|
151
157
|
end
|
@@ -11,6 +11,12 @@ describe "Kernel#instance_variables" do
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
context 'for non-empty string' do
|
15
|
+
it 'returns blank array' do
|
16
|
+
expect('test'.instance_variables).to eq([])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
14
20
|
context 'for hash' do
|
15
21
|
it 'returns blank array' do
|
16
22
|
expect({}.instance_variables).to eq([])
|
@@ -31,6 +37,24 @@ describe "Kernel#instance_variables" do
|
|
31
37
|
end
|
32
38
|
end
|
33
39
|
|
40
|
+
context 'for non-empty hash' do
|
41
|
+
it 'returns blank array' do
|
42
|
+
expect({ 1 => 2 }.instance_variables).to eq([])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'for array' do
|
47
|
+
it 'returns blank array' do
|
48
|
+
expect([].instance_variables).to eq([])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'for non-empty array' do
|
53
|
+
it 'returns blank array' do
|
54
|
+
expect((1..20).to_a.instance_variables).to eq([])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
34
58
|
context 'for object' do
|
35
59
|
it 'returns blank array' do
|
36
60
|
expect(Object.new.instance_variables).to eq([])
|
@@ -67,4 +91,20 @@ describe "Kernel#instance_variables" do
|
|
67
91
|
end
|
68
92
|
end
|
69
93
|
end
|
94
|
+
|
95
|
+
context 'for a class' do
|
96
|
+
it 'returns blank array' do
|
97
|
+
expect(Class.new.instance_variables).to eq([])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'for a class with nested constant' do
|
102
|
+
class ClassWithConstantWithoutIvar
|
103
|
+
A = 1
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'returns blank array' do
|
107
|
+
expect(ClassWithConstantWithoutIvar.instance_variables).to eq([])
|
108
|
+
end
|
109
|
+
end
|
70
110
|
end
|
@@ -11,4 +11,10 @@ describe "Ternary condition operator" do
|
|
11
11
|
# this could be interpreted as a Ruby 1.9 symbol hash key
|
12
12
|
(true ?'str':'another str').should == 'str'
|
13
13
|
end
|
14
|
+
|
15
|
+
it "doesn't interpret ?? as an identifier" do
|
16
|
+
obj = mock("object with a query method")
|
17
|
+
obj.should_receive("m?").and_return(true)
|
18
|
+
(obj.m?? 1 : 2).should == 1
|
19
|
+
end
|
14
20
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MarshalExtension
|
4
|
+
end
|
5
|
+
|
6
|
+
class MarshalUserRegexp < Regexp
|
7
|
+
end
|
8
|
+
|
9
|
+
class UserMarshal
|
10
|
+
attr_reader :data
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@data = 'stuff'
|
14
|
+
end
|
15
|
+
def marshal_dump() 'data' end
|
16
|
+
def marshal_load(data) @data = data end
|
17
|
+
def ==(other) self.class === other and @data == other.data end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'Marshal.dump' do
|
21
|
+
it 'dumps non-empty Array' do
|
22
|
+
expect(Marshal.dump(['a', 1, 2])).to eq("\u0004\b[\b\"\u0006ai\u0006i\a")
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'dumps case-sensitive regexp' do
|
26
|
+
expect(Marshal.dump(/\w+/)).to eq("\u0004\b/\b\\w+\u0000")
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'dumps case-insensitive regexp' do
|
30
|
+
expect(Marshal.dump(/\w+/i)).to eq("\u0004\b/\b\\w+\u0001")
|
31
|
+
end
|
32
|
+
|
33
|
+
it "dumps a Float" do
|
34
|
+
Marshal.dump(123.4567).should == "\004\bf\r123.4567"
|
35
|
+
Marshal.dump(-0.841).should == "\x04\bf\v-0.841"
|
36
|
+
Marshal.dump(9876.345).should == "\004\bf\r9876.345"
|
37
|
+
Marshal.dump(Float::INFINITY).should == "\004\bf\binf"
|
38
|
+
Marshal.dump(-Float::INFINITY).should == "\004\bf\t-inf"
|
39
|
+
Marshal.dump(Float::NAN).should == "\004\bf\bnan"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "dumps a Regexp with flags" do
|
43
|
+
Marshal.dump(/\w/im).should == "\x04\b/\a\\w\u0005"
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'dumps an extended Regexp' do
|
47
|
+
Marshal.dump(/\w/.extend(MarshalExtension)).should == "\x04\be:\u0015MarshalExtension/\a\\w\u0000"
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'dumps object#marshal_dump when object responds to #marshal_dump' do
|
51
|
+
Marshal.dump(UserMarshal.new).should == "\u0004\bU:\u0010UserMarshal\"\tdata"
|
52
|
+
end
|
53
|
+
end
|
@@ -4,12 +4,46 @@ require 'opal-source-maps'
|
|
4
4
|
describe Opal::SourceMap do
|
5
5
|
before do
|
6
6
|
pathname = 'foo.rb'
|
7
|
-
compiler = Opal::Compiler.new("
|
7
|
+
compiler = Opal::Compiler.new("def foo\n123\nend", :file => pathname)
|
8
8
|
@source = compiler.compile
|
9
9
|
@map = Opal::SourceMap.new(compiler.fragments, pathname)
|
10
10
|
end
|
11
11
|
|
12
|
+
def js_line_for(code)
|
13
|
+
index = @source.split("\n").index {|line| line.include?(code)}
|
14
|
+
# 1 based line numbers
|
15
|
+
index + 1
|
16
|
+
end
|
17
|
+
|
18
|
+
def parsed_map
|
19
|
+
SourceMap::Map.from_json(@map.as_json.to_json)
|
20
|
+
end
|
21
|
+
|
22
|
+
def mappings
|
23
|
+
mappings = []
|
24
|
+
# mappings is not exposed on the ext library
|
25
|
+
parsed_map.each do |mapping|
|
26
|
+
mappings << mapping
|
27
|
+
end
|
28
|
+
mappings
|
29
|
+
end
|
30
|
+
|
12
31
|
it 'does not blow while generating the map' do
|
13
32
|
lambda { @map.as_json }.should_not raise_error
|
14
33
|
end
|
34
|
+
|
35
|
+
it 'parses the map without error' do
|
36
|
+
parsed_map.should_not be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'identifies line numbers' do
|
40
|
+
match = mappings.find {|map| map.original.line == 2 }
|
41
|
+
# as of Opal 0.10
|
42
|
+
match.generated.line.should == js_line_for('return 123')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'uses method names' do
|
46
|
+
match = mappings.find {|map| map.original.line == 2 }
|
47
|
+
match.name.should == 'foo'
|
48
|
+
end
|
15
49
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module JavaScriptAPIFixtures
|
2
|
+
class A
|
3
|
+
end
|
4
|
+
|
5
|
+
class A::B
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'spec_helper'
|
10
|
+
require 'native'
|
11
|
+
|
12
|
+
describe "JavaScript API" do
|
13
|
+
it "allows to acces scopes on `Opal` with dots (regression for #1418)" do
|
14
|
+
`Opal.JavaScriptAPIFixtures.A.B`.should == JavaScriptAPIFixtures::A::B
|
15
|
+
end
|
16
|
+
end
|
data/spec/ruby_specs
CHANGED
@@ -15,12 +15,12 @@ ruby/core/exception
|
|
15
15
|
|
16
16
|
ruby/core/false
|
17
17
|
ruby/core/fixnum
|
18
|
+
ruby/core/file/basename_spec
|
19
|
+
ruby/core/file/dirname_spec
|
18
20
|
ruby/core/file/extname_spec
|
19
21
|
ruby/core/float
|
20
22
|
ruby/core/hash
|
21
|
-
|
22
23
|
ruby/core/integer
|
23
|
-
!ruby/core/integer/times_spec
|
24
24
|
|
25
25
|
ruby/core/kernel
|
26
26
|
!ruby/core/kernel/abort_spec
|
@@ -69,6 +69,11 @@ ruby/core/method
|
|
69
69
|
|
70
70
|
ruby/core/module
|
71
71
|
|
72
|
+
ruby/core/marshal/load_spec
|
73
|
+
ruby/core/marshal/dump_spec
|
74
|
+
ruby/core/marshal/major_version_spec
|
75
|
+
ruby/core/marshal/minor_version_spec
|
76
|
+
|
72
77
|
ruby/core/nil
|
73
78
|
ruby/core/numeric
|
74
79
|
|
@@ -95,10 +100,8 @@ ruby/core/unboundmethod
|
|
95
100
|
ruby/language
|
96
101
|
!ruby/language/constants_spec
|
97
102
|
!ruby/language/execution_spec
|
98
|
-
!ruby/language/hash_spec
|
99
103
|
!ruby/language/next_spec
|
100
104
|
!ruby/language/return_spec
|
101
|
-
!ruby/language/send_spec
|
102
105
|
!ruby/language/yield_spec
|
103
106
|
|
104
107
|
ruby/library/base64
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module MatchHelpers
|
2
|
+
class MatchMatcher
|
3
|
+
def initialize(expected)
|
4
|
+
fail "Expected #{expected} to be a Regexp!" unless expected.is_a?(Regexp)
|
5
|
+
@expected = expected
|
6
|
+
end
|
7
|
+
|
8
|
+
def matches?(actual)
|
9
|
+
@actual = actual
|
10
|
+
@expected.match(@actual)
|
11
|
+
end
|
12
|
+
|
13
|
+
def failure_message
|
14
|
+
["Expected #{@actual.inspect} (#{@actual.class})",
|
15
|
+
"to match #{@expected}"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def negative_failure_message
|
19
|
+
["Expected #{@actual.inspect} (#{@actual.class})",
|
20
|
+
"not to match #{@expected}"]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class EndWithHelper
|
25
|
+
def initialize(expected)
|
26
|
+
@expected = expected
|
27
|
+
end
|
28
|
+
|
29
|
+
def matches?(actual)
|
30
|
+
@actual = actual
|
31
|
+
@actual.end_with?(@expected)
|
32
|
+
end
|
33
|
+
|
34
|
+
def failure_message
|
35
|
+
["Expected #{@actual.inspect} (#{@actual.class})",
|
36
|
+
"to end with #{@expected}"]
|
37
|
+
end
|
38
|
+
|
39
|
+
def negative_failure_message
|
40
|
+
["Expected #{@actual.inspect} (#{@actual.class})",
|
41
|
+
"not to end with #{@expected}"]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if !defined? RSpec
|
47
|
+
class Object
|
48
|
+
def match(expected)
|
49
|
+
MatchHelpers::MatchMatcher.new(expected)
|
50
|
+
end
|
51
|
+
|
52
|
+
def end_with(expected)
|
53
|
+
MatchHelpers::EndWithHelper.new(expected)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
data/stdlib/opal-parser.rb
CHANGED
@@ -5,7 +5,9 @@ require 'opal/version'
|
|
5
5
|
module Kernel
|
6
6
|
def eval(str)
|
7
7
|
str = Opal.coerce_to!(str, String, :to_str)
|
8
|
-
|
8
|
+
default_eval_options = { file: '(eval)', eval: true }
|
9
|
+
compiling_options = __OPAL_COMPILER_CONFIG__.merge(default_eval_options)
|
10
|
+
code = Opal.compile str, compiling_options
|
9
11
|
%x{
|
10
12
|
return (function(self) {
|
11
13
|
return eval(#{code});
|
data/stdlib/pathname.rb
CHANGED
@@ -1,7 +1,23 @@
|
|
1
|
+
require 'corelib/comparable'
|
2
|
+
|
3
|
+
# Portions from Author:: Tanaka Akira <akr@m17n.org>
|
1
4
|
class Pathname
|
5
|
+
include Comparable
|
6
|
+
SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/
|
7
|
+
|
2
8
|
def initialize(path)
|
3
|
-
|
4
|
-
|
9
|
+
if Pathname === path
|
10
|
+
@path = path.path.to_s
|
11
|
+
elsif path.respond_to?(:to_path)
|
12
|
+
@path = path.to_path
|
13
|
+
elsif path.is_a?(String)
|
14
|
+
@path = path
|
15
|
+
elsif path.nil?
|
16
|
+
raise TypeError, 'no implicit conversion of nil into String'
|
17
|
+
else
|
18
|
+
raise TypeError, "no implicit conversion of #{path.class} into String"
|
19
|
+
end
|
20
|
+
raise ArgumentError if @path == "\0"
|
5
21
|
end
|
6
22
|
|
7
23
|
attr_reader :path
|
@@ -11,11 +27,25 @@ class Pathname
|
|
11
27
|
end
|
12
28
|
|
13
29
|
def absolute?
|
14
|
-
|
30
|
+
!relative?
|
15
31
|
end
|
16
32
|
|
17
33
|
def relative?
|
18
|
-
|
34
|
+
path = @path
|
35
|
+
while r = chop_basename(path)
|
36
|
+
path, = r
|
37
|
+
end
|
38
|
+
path == ''
|
39
|
+
end
|
40
|
+
|
41
|
+
def chop_basename(path) # :nodoc:
|
42
|
+
base = File.basename(path)
|
43
|
+
# ruby uses /^#{SEPARATOR_PAT}?$/o but having issues with interpolation
|
44
|
+
if Regexp.new("^#{Pathname::SEPARATOR_PAT.source}?$") =~ base
|
45
|
+
return nil
|
46
|
+
else
|
47
|
+
return path[0, path.rindex(base)], base
|
48
|
+
end
|
19
49
|
end
|
20
50
|
|
21
51
|
def root?
|
@@ -50,11 +80,52 @@ class Pathname
|
|
50
80
|
|
51
81
|
def +(other)
|
52
82
|
other = Pathname.new(other) unless Pathname === other
|
53
|
-
Pathname.new(
|
83
|
+
Pathname.new(plus(@path, other.to_s))
|
84
|
+
end
|
85
|
+
|
86
|
+
def plus(path1, path2) # -> path # :nodoc:
|
87
|
+
prefix2 = path2
|
88
|
+
index_list2 = []
|
89
|
+
basename_list2 = []
|
90
|
+
while r2 = chop_basename(prefix2)
|
91
|
+
prefix2, basename2 = r2
|
92
|
+
index_list2.unshift prefix2.length
|
93
|
+
basename_list2.unshift basename2
|
94
|
+
end
|
95
|
+
return path2 if prefix2 != ''
|
96
|
+
prefix1 = path1
|
97
|
+
while true
|
98
|
+
while !basename_list2.empty? && basename_list2.first == '.'
|
99
|
+
index_list2.shift
|
100
|
+
basename_list2.shift
|
101
|
+
end
|
102
|
+
break unless r1 = chop_basename(prefix1)
|
103
|
+
prefix1, basename1 = r1
|
104
|
+
next if basename1 == '.'
|
105
|
+
if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..'
|
106
|
+
prefix1 = prefix1 + basename1
|
107
|
+
break
|
108
|
+
end
|
109
|
+
index_list2.shift
|
110
|
+
basename_list2.shift
|
111
|
+
end
|
112
|
+
r1 = chop_basename(prefix1)
|
113
|
+
if !r1 && /#{SEPARATOR_PAT}/o =~ File.basename(prefix1)
|
114
|
+
while !basename_list2.empty? && basename_list2.first == '..'
|
115
|
+
index_list2.shift
|
116
|
+
basename_list2.shift
|
117
|
+
end
|
118
|
+
end
|
119
|
+
if !basename_list2.empty?
|
120
|
+
suffix2 = path2[index_list2.first..-1]
|
121
|
+
r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2
|
122
|
+
else
|
123
|
+
r1 ? prefix1 : File.dirname(prefix1)
|
124
|
+
end
|
54
125
|
end
|
55
126
|
|
56
127
|
def join(*args)
|
57
|
-
args.
|
128
|
+
return self if args.empty?
|
58
129
|
result = args.pop
|
59
130
|
result = Pathname.new(result) unless Pathname === result
|
60
131
|
return result if result.absolute?
|
@@ -63,9 +134,36 @@ class Pathname
|
|
63
134
|
result = arg + result
|
64
135
|
return result if result.absolute?
|
65
136
|
}
|
66
|
-
result
|
137
|
+
self + result
|
138
|
+
end
|
139
|
+
|
140
|
+
def split
|
141
|
+
[ dirname, basename ]
|
142
|
+
end
|
143
|
+
|
144
|
+
def dirname
|
145
|
+
Pathname.new(File.dirname(@path))
|
67
146
|
end
|
68
147
|
|
148
|
+
def basename
|
149
|
+
Pathname.new(File.basename(@path))
|
150
|
+
end
|
151
|
+
|
152
|
+
def directory?
|
153
|
+
File.directory?(@path)
|
154
|
+
end
|
155
|
+
|
156
|
+
def extname
|
157
|
+
File.extname(@path)
|
158
|
+
end
|
159
|
+
|
160
|
+
def <=>(other)
|
161
|
+
self.path <=> other.path
|
162
|
+
end
|
163
|
+
|
164
|
+
alias eql? ==
|
165
|
+
alias === ==
|
166
|
+
|
69
167
|
alias :to_str :to_path
|
70
168
|
alias :to_s :to_path
|
71
169
|
end
|