opal-sprockets 0.4.5.1.0.3.7 → 1.0.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.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +26 -0
- data/CHANGELOG.md +39 -0
- data/bin/console +14 -0
- data/bin/rake +6 -0
- data/bin/rspec +29 -0
- data/bin/setup +8 -0
- data/example/Gemfile.lock +1 -1
- data/lib/opal/environment.rb +4 -0
- data/lib/opal/processor.rb +4 -0
- data/lib/opal/sprockets.rb +13 -81
- data/lib/opal/sprockets/assets_helper.rb +78 -0
- data/lib/opal/sprockets/environment.rb +12 -14
- data/lib/opal/sprockets/erb.rb +12 -16
- data/lib/opal/sprockets/mime_types.rb +19 -0
- data/lib/opal/sprockets/processor.rb +111 -114
- data/lib/opal/sprockets/server.rb +94 -98
- data/lib/opal/sprockets/version.rb +1 -8
- data/opal-sprockets.gemspec +38 -24
- data/spec/fixtures/complex_sprockets.js.rb.erb +1 -1
- data/spec/fixtures/{jst_file.js.jst → jst_file.jst.ejs} +0 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/sprockets/erb_spec.rb +15 -25
- data/spec/sprockets/processor_spec.rb +53 -57
- data/spec/sprockets/server_spec.rb +95 -25
- data/spec/sprockets_spec.rb +9 -9
- data/spec/tilt/opal_spec.rb +1 -1
- metadata +69 -27
- data/.travis.yml +0 -21
- data/lib/opal/sprockets/path_reader.rb +0 -36
- data/spec/shared/path_finder_shared.rb +0 -19
- data/spec/shared/path_reader_shared.rb +0 -31
- data/spec/sprockets/path_reader_spec.rb +0 -41
@@ -1,12 +1,5 @@
|
|
1
1
|
module Opal
|
2
2
|
module Sprockets
|
3
|
-
|
4
|
-
OPAL_VERSION = '1.0.0'
|
5
|
-
SPROCKETS_VERSION = '3.7'
|
6
|
-
|
7
|
-
# Just keep the first two segments of dependencies' versions
|
8
|
-
v = -> v { v.split('.')[0..1].compact.join('.') }
|
9
|
-
|
10
|
-
VERSION = "#{BASE_VERSION}.#{v[OPAL_VERSION]}.#{v[SPROCKETS_VERSION]}"
|
3
|
+
VERSION = '1.0.0'
|
11
4
|
end
|
12
5
|
end
|
data/opal-sprockets.gemspec
CHANGED
@@ -1,25 +1,39 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
1
|
+
require_relative 'lib/opal/sprockets/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'opal-sprockets'
|
5
|
+
spec.version = Opal::Sprockets::VERSION
|
6
|
+
spec.authors = ['Elia Schito', 'Adam Beynon']
|
7
|
+
spec.email = 'elia@schito.me'
|
8
|
+
|
9
|
+
spec.summary = 'Sprockets support for Opal.'
|
10
|
+
spec.homepage = 'https://github.com/opal/opal-sprockets#readme'
|
11
|
+
spec.license = 'MIT'
|
12
|
+
|
13
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
14
|
+
spec.metadata['source_code_uri'] = 'https://github.com/opal/opal-sprockets'
|
15
|
+
spec.metadata['changelog_uri'] = 'https://github.com/opal/opal-sprockets/blob/master/CHANGELOG.md'
|
16
|
+
|
17
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.5', '< 3.1')
|
18
|
+
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
|
22
|
+
|
23
|
+
spec.files = files.grep_v(%r{^(test|spec|features)/})
|
24
|
+
spec.test_files = files.grep(%r{^(test|spec|features)/})
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
|
29
|
+
spec.add_dependency 'sprockets', "~> 4.0"
|
30
|
+
spec.add_dependency 'opal', [">= 1.0", "< 1.2"]
|
31
|
+
spec.add_dependency 'tilt', '>= 1.4'
|
32
|
+
|
33
|
+
spec.add_development_dependency 'rake'
|
34
|
+
spec.add_development_dependency 'rspec'
|
35
|
+
spec.add_development_dependency 'rack-test'
|
36
|
+
spec.add_development_dependency 'sourcemap'
|
37
|
+
spec.add_development_dependency 'ejs'
|
38
|
+
spec.add_development_dependency 'pry'
|
25
39
|
end
|
File without changes
|
data/spec/spec_helper.rb
CHANGED
data/spec/sprockets/erb_spec.rb
CHANGED
@@ -1,38 +1,28 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'opal/sprockets/erb'
|
3
3
|
|
4
|
-
describe Opal::ERB
|
4
|
+
describe Opal::Sprockets::ERB do
|
5
5
|
let(:pathname) { Pathname("/Code/app/mylib/opal/foo.#{ext}") }
|
6
|
-
let(:
|
7
|
-
|
8
|
-
:
|
9
|
-
|
10
|
-
engines: double(keys: %w[.rb .js .erb .opal]),
|
11
|
-
) }
|
12
|
-
let(:sprockets_context) { double(Sprockets::Context,
|
13
|
-
logical_path: "foo.#{ext}",
|
14
|
-
environment: environment,
|
15
|
-
pathname: pathname,
|
6
|
+
let(:data) { %{<% print("") %><a href="<%= url %>"><%= name %></a>} }
|
7
|
+
let(:input) { {
|
8
|
+
environment: Opal::Sprockets::Environment.new.tap {|e| e.append_path "/Code/app/mylib/opal"},
|
9
|
+
data: data,
|
16
10
|
filename: pathname.to_s,
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
let(:template) { described_class.new { |t| %Q{<% print("") %><a href="<%= url %>"><%= name %></a>} } }
|
22
|
-
before { allow(sprockets_context).to receive(:require_asset) {|asset| required_assets << asset } }
|
23
|
-
|
11
|
+
load_path: "/Code/app/mylib/opal",
|
12
|
+
metadata: {},
|
13
|
+
cache: Sprockets::Cache.new
|
14
|
+
} }
|
24
15
|
let(:ext) { 'opalerb' }
|
25
16
|
|
26
|
-
it "is registered for '.opalerb' files" do
|
27
|
-
expect(Tilt["test.#{ext}"]).to eq(described_class)
|
28
|
-
end
|
29
|
-
|
30
17
|
it 'renders the template' do
|
31
|
-
|
18
|
+
result = described_class.call(input)
|
19
|
+
|
20
|
+
expect(result[:data]).to include('"<a href=\""')
|
32
21
|
end
|
33
22
|
|
34
23
|
it 'implicitly requires "erb"' do
|
35
|
-
|
36
|
-
|
24
|
+
result = described_class.call(input)
|
25
|
+
|
26
|
+
expect(result[:required].first).to include("stdlib/erb.rb")
|
37
27
|
end
|
38
28
|
end
|
@@ -1,79 +1,75 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'opal/sprockets/processor'
|
3
3
|
|
4
|
-
describe Opal::Processor do
|
4
|
+
describe Opal::Sprockets::Processor do
|
5
5
|
let(:pathname) { Pathname("/Code/app/mylib/opal/foo.#{ext}") }
|
6
|
-
let(:
|
7
|
-
|
8
|
-
:
|
9
|
-
|
10
|
-
engines: double(keys: %w[.rb .js .opal]),
|
11
|
-
) }
|
12
|
-
let(:sprockets_context) { double(Sprockets::Context,
|
13
|
-
logical_path: "foo.#{ext}",
|
14
|
-
environment: environment,
|
15
|
-
pathname: pathname,
|
6
|
+
let(:data) { "foo" }
|
7
|
+
let(:input) { {
|
8
|
+
environment: Opal::Sprockets::Environment.new.tap {|e| e.append_path "/Code/app/mylib/opal"},
|
9
|
+
data: data,
|
16
10
|
filename: pathname.to_s,
|
17
|
-
|
18
|
-
|
19
|
-
|
11
|
+
load_path: "/Code/app/mylib/opal",
|
12
|
+
metadata: {},
|
13
|
+
cache: Sprockets::Cache.new
|
14
|
+
} }
|
15
|
+
|
16
|
+
before do
|
17
|
+
allow(Sprockets::SourceMapUtils).to receive(:format_source_map)
|
18
|
+
allow(Sprockets::SourceMapUtils).to receive(:combine_source_maps)
|
19
|
+
end
|
20
20
|
|
21
21
|
%w[.rb .opal].each do |ext|
|
22
|
-
|
22
|
+
describe %{with extension "#{ext}"} do
|
23
|
+
let(:ext) { ext }
|
23
24
|
|
24
|
-
describe %Q{with extension "#{ext}"} do
|
25
25
|
it "is registered for '#{ext}' files" do
|
26
|
-
|
26
|
+
mime_type = Sprockets.mime_types.find { |_,v| v[:extensions].include? ".rb" }.first
|
27
|
+
transformers = Sprockets.transformers[mime_type]
|
28
|
+
transformer = transformers["application/javascript"]
|
29
|
+
expect(transformer.processors).to include(described_class)
|
27
30
|
end
|
28
31
|
|
29
|
-
it "compiles
|
30
|
-
|
31
|
-
|
32
|
+
it "compiles the code" do
|
33
|
+
result = described_class.call(input.merge data: "puts 'Hello, World!'\n")
|
34
|
+
|
35
|
+
expect(result[:data]).to include('"Hello, World!"')
|
32
36
|
end
|
33
|
-
end
|
34
|
-
end
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
e.run
|
40
|
-
config.stubbed_files.clear
|
41
|
-
end
|
38
|
+
describe '.stubbed_files' do
|
39
|
+
it 'requires non-stubbed files' do
|
40
|
+
result = described_class.call(input.merge(data: 'require "set"'))
|
42
41
|
|
43
|
-
|
44
|
-
|
45
|
-
let(:config) { Opal::Config }
|
42
|
+
expect(result[:required].first).to include("stdlib/set.rb")
|
43
|
+
end
|
46
44
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
45
|
+
it 'skips require of stubbed file' do
|
46
|
+
Opal::Config.stubbed_files << 'set'
|
47
|
+
result = described_class.call(input.merge(data: "require 'set'"))
|
51
48
|
|
52
|
-
|
53
|
-
|
54
|
-
expect(sprockets_context).not_to receive(:require_asset).with(stubbed_file)
|
55
|
-
template.render(sprockets_context)
|
56
|
-
end
|
49
|
+
expect(result[:required]).not_to include("set.rb")
|
50
|
+
end
|
57
51
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
allow(environment).to receive(:[]).with('bar.js') { asset }
|
62
|
-
allow(environment).to receive(:engines) { {'.rb' => described_class, '.opal' => described_class} }
|
52
|
+
it 'marks a stubbed file as loaded' do
|
53
|
+
Opal::Config.stubbed_files << 'set'
|
54
|
+
result = described_class.call(input.merge(data: "require 'set'"))
|
63
55
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
56
|
+
expect(result[:data]).not_to include(::Opal::Sprockets.load_asset('set'))
|
57
|
+
end
|
58
|
+
end
|
68
59
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
60
|
+
describe '.cache_key' do
|
61
|
+
it 'can be reset' do
|
62
|
+
old_cache_key = described_class.cache_key
|
63
|
+
Opal::Config.arity_check_enabled = !Opal::Config.arity_check_enabled
|
64
|
+
stale_cache_key = described_class.cache_key
|
65
|
+
|
66
|
+
described_class.reset_cache_key!
|
67
|
+
reset_cache_key = described_class.cache_key
|
68
|
+
|
69
|
+
expect(stale_cache_key).to eq(old_cache_key)
|
70
|
+
expect(reset_cache_key).not_to eq(old_cache_key)
|
71
|
+
end
|
72
|
+
end
|
77
73
|
end
|
78
74
|
end
|
79
75
|
end
|
@@ -20,60 +20,130 @@ describe Opal::Sprockets::Server do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'serves assets with complex sprockets requires' do
|
23
|
-
asset = app.sprockets['complex_sprockets']
|
23
|
+
asset = app.sprockets['complex_sprockets', accept: "application/javascript"]
|
24
24
|
expect(asset).to be_truthy
|
25
25
|
|
26
|
-
assets = asset.
|
27
|
-
sub_asset.
|
26
|
+
assets = asset.metadata[:included].map do |sub_asset|
|
27
|
+
URI.parse(sub_asset).path.split(%r'/fixtures/|/stdlib/').last
|
28
28
|
end
|
29
29
|
|
30
30
|
%w[
|
31
|
-
base64.
|
32
|
-
no_requires.
|
33
|
-
jst_file.
|
34
|
-
required_tree_test/required_file1.
|
35
|
-
required_tree_test/required_file2.
|
31
|
+
base64.rb
|
32
|
+
no_requires.rb
|
33
|
+
jst_file.jst.ejs
|
34
|
+
required_tree_test/required_file1.rb
|
35
|
+
required_tree_test/required_file2.rb
|
36
36
|
file_with_directives.js
|
37
37
|
].each do |logical_path|
|
38
38
|
expect(assets).to include(logical_path)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
+
it 'recompiles the asset when its dependencies change' do
|
43
|
+
does_include = proc do |it, what, what_not=[], what_else=nil, what_else_not=nil|
|
44
|
+
expect(it).to include what_else if what_else
|
45
|
+
expect(it).not_to include what_else_not if what_else_not
|
46
|
+
what.each { |i| expect(it).to include %{modules["required_tree_test/required_file#{i}"]} }
|
47
|
+
what_not.each { |i| expect(it).not_to include %{modules["required_tree_test/required_file#{i}"]} }
|
48
|
+
end
|
49
|
+
|
50
|
+
["default", "debug"].each do |pipeline|
|
51
|
+
asset = app.sprockets['require_tree_test', pipeline: pipeline, accept: "application/javascript"]
|
52
|
+
expect(asset).to be_truthy
|
53
|
+
does_include.(asset.to_s, [1,2], [3], nil, "UNIQUESTRING")
|
54
|
+
get "/assets/require_tree_test.#{pipeline}.js"
|
55
|
+
does_include.(last_response.body, [1,2], [3], nil, "UNIQUESTRING")
|
56
|
+
|
57
|
+
sleep 1 # Make sure to modify mtime
|
58
|
+
|
59
|
+
File.write(__dir__+"/../fixtures/required_tree_test/required_file2.rb", "p 'UNIQUESTRING1'")
|
60
|
+
|
61
|
+
asset = app.sprockets['require_tree_test', pipeline: pipeline, accept: "application/javascript"]
|
62
|
+
expect(asset).to be_truthy
|
63
|
+
does_include.(asset.to_s, [1,2], [3], "UNIQUESTRING1")
|
64
|
+
get "/assets/require_tree_test.#{pipeline}.js"
|
65
|
+
does_include.(last_response.body, [1,2], [3], "UNIQUESTRING1")
|
66
|
+
|
67
|
+
sleep 1 # Make sure to modify mtime
|
68
|
+
|
69
|
+
File.write(__dir__+"/../fixtures/required_tree_test/required_file2.rb", "p 'UNIQUESTRING2'")
|
70
|
+
|
71
|
+
asset = app.sprockets['require_tree_test', pipeline: pipeline, accept: "application/javascript"]
|
72
|
+
expect(asset).to be_truthy
|
73
|
+
does_include.(asset.to_s, [1,2], [3], "UNIQUESTRING2")
|
74
|
+
get "/assets/require_tree_test.#{pipeline}.js"
|
75
|
+
does_include.(last_response.body, [1,2], [3], "UNIQUESTRING2")
|
76
|
+
|
77
|
+
sleep 1 # Make sure to modify mtime
|
78
|
+
|
79
|
+
File.write(__dir__+"/../fixtures/required_tree_test/required_file2.rb", "p 'UNIQUESTRING3'")
|
80
|
+
File.write(__dir__+"/../fixtures/required_tree_test/required_file3.rb", "p 3")
|
81
|
+
|
82
|
+
asset = app.sprockets['require_tree_test', pipeline: pipeline, accept: "application/javascript"]
|
83
|
+
expect(asset).to be_truthy
|
84
|
+
does_include.(asset.to_s, [1,2,3], [], "UNIQUESTRING3")
|
85
|
+
get "/assets/require_tree_test.#{pipeline}.js"
|
86
|
+
does_include.(last_response.body, [1,2], [], "UNIQUESTRING3") # fails with 3 - it doesn't get new files
|
87
|
+
|
88
|
+
sleep 1 # Make sure to modify mtime
|
89
|
+
ensure
|
90
|
+
File.write(__dir__+"/../fixtures/required_tree_test/required_file2.rb", "p 2\n")
|
91
|
+
File.unlink(__dir__+"/../fixtures/required_tree_test/required_file3.rb") rescue nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
42
95
|
describe 'source maps' do
|
43
|
-
RSpec::Matchers.define :
|
96
|
+
RSpec::Matchers.define :include_source_map do
|
44
97
|
match do |actual_response|
|
45
98
|
actual_response.ok? &&
|
46
|
-
actual_response.body.lines.last.start_with?('//# sourceMappingURL=
|
99
|
+
actual_response.body.lines.last.start_with?('//# sourceMappingURL=')
|
47
100
|
end
|
48
101
|
end
|
49
102
|
|
50
|
-
def
|
103
|
+
def extract_map(path, response)
|
51
104
|
last_line = response.body.lines.last
|
52
|
-
|
53
|
-
|
105
|
+
if last_line.start_with? "//# sourceMappingURL=data:application/json;base64,"
|
106
|
+
b64_encoded = last_line.split('//# sourceMappingURL=data:application/json;base64,', 2)[1]
|
107
|
+
json_string = Base64.decode64(b64_encoded)
|
108
|
+
else
|
109
|
+
map_file = last_line.split('//# sourceMappingURL=', 2)[1].chomp
|
110
|
+
map_file = relative_path(path, map_file)
|
111
|
+
map_file = get map_file
|
112
|
+
json_string = map_file.body
|
113
|
+
end
|
54
114
|
JSON.parse(json_string, symbolize_names: true)
|
55
115
|
end
|
56
116
|
|
117
|
+
def relative_path(path, file)
|
118
|
+
URI.join("http://example.com/", path, file).path
|
119
|
+
end
|
120
|
+
|
57
121
|
it 'serves map for a top level file' do
|
58
|
-
|
59
|
-
|
122
|
+
path = '/assets/opal_file.debug.js'
|
123
|
+
get path
|
124
|
+
expect(last_response).to include_source_map
|
60
125
|
|
61
|
-
map =
|
126
|
+
map = extract_map(path, last_response)
|
62
127
|
|
63
|
-
expect(map[:
|
64
|
-
expect(map[:
|
128
|
+
expect(map[:file]).to eq('opal_file.rb')
|
129
|
+
expect(map[:sections].last[:map][:sources]).to eq(['opal_file.source.rb'])
|
130
|
+
|
131
|
+
expect(get(relative_path(path, map[:sections].last[:map][:sources][0])).body.chomp)
|
132
|
+
.to eq("require 'opal'\nputs 'hi from opal!'")
|
65
133
|
end
|
66
134
|
|
67
135
|
it 'serves map for a subfolder file' do
|
68
|
-
|
69
|
-
|
136
|
+
path = '/assets/source_map/subfolder/other_file.debug.js'
|
137
|
+
get path
|
138
|
+
expect(last_response).to include_source_map
|
139
|
+
|
140
|
+
map = extract_map(path, last_response)
|
70
141
|
|
71
|
-
map
|
142
|
+
expect(map[:file]).to eq('source_map/subfolder/other_file.rb')
|
143
|
+
expect(map[:sections].first[:map][:sources]).to eq(['other_file.source.rb'])
|
72
144
|
|
73
|
-
expect(map[:sources])
|
74
|
-
.to eq(
|
75
|
-
.or eq(['source_map/subfolder/other_file'])
|
76
|
-
expect(map[:sourcesContent]).to eq(["puts 'other!'\n"])
|
145
|
+
expect(get(relative_path(path, map[:sections][0][:map][:sources][0])).body.chomp)
|
146
|
+
.to eq("puts 'other!'")
|
77
147
|
end
|
78
148
|
end
|
79
149
|
end
|
data/spec/sprockets_spec.rb
CHANGED
@@ -8,20 +8,20 @@ describe Opal::Sprockets do
|
|
8
8
|
describe '.load_asset' do
|
9
9
|
it 'loads the main asset' do
|
10
10
|
code = described_class.load_asset('console')
|
11
|
-
expect(code).to include('Opal.
|
11
|
+
expect(code).to include('Opal.require("console");')
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'marks as loaded stubs and all non-opal assets' do
|
15
15
|
allow(Opal::Config).to receive(:stubbed_files) { %w[foo bar] }
|
16
16
|
|
17
17
|
code = described_class.load_asset('baz')
|
18
|
-
expect(code).to include(
|
19
|
-
expect(code).to include('Opal.
|
18
|
+
expect(code).to include(%{Opal.loaded(["foo","bar"].concat(typeof(OpalLoaded) === "undefined" ? [] : OpalLoaded));})
|
19
|
+
expect(code).to include('Opal.require("baz");')
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'tries to load an asset if it is registered as opal module' do
|
23
23
|
code = described_class.load_asset('foo')
|
24
|
-
expect(code).to include('Opal.
|
24
|
+
expect(code).to include('Opal.require("foo");')
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'warns the user that passing an env is not needed, only once' do
|
@@ -35,16 +35,16 @@ describe Opal::Sprockets do
|
|
35
35
|
|
36
36
|
it 'accepts multiple names' do
|
37
37
|
code = described_class.load_asset('foo', 'bar')
|
38
|
-
expect(code).to include('Opal.
|
39
|
-
expect(code).to include('Opal.
|
38
|
+
expect(code).to include('Opal.require("foo");')
|
39
|
+
expect(code).to include('Opal.require("bar");')
|
40
40
|
end
|
41
41
|
|
42
42
|
it 'detects deprecated env with multiple names' do
|
43
43
|
code = described_class.load_asset('foo', 'bar', env)
|
44
44
|
expect(code).to eq([
|
45
|
-
'Opal.loaded(OpalLoaded
|
46
|
-
'Opal.
|
47
|
-
'Opal.
|
45
|
+
'Opal.loaded(typeof(OpalLoaded) === "undefined" ? [] : OpalLoaded);',
|
46
|
+
'Opal.require("foo");',
|
47
|
+
'Opal.require("bar");',
|
48
48
|
].join("\n"))
|
49
49
|
end
|
50
50
|
end
|